Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
authorLinus Torvalds <torvalds@woody.osdl.org>
Fri, 8 Dec 2006 19:21:55 +0000 (11:21 -0800)
committerLinus Torvalds <torvalds@woody.osdl.org>
Fri, 8 Dec 2006 19:21:55 +0000 (11:21 -0800)
* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6:
  [S390] Poison init section before freeing it.
  [S390] Use add_active_range() and free_area_init_nodes().
  [S390] Virtual memmap for s390.
  [S390] Update documentation for dynamic subchannel mapping.
  [S390] Use dev->groups for adding/removing the subchannel attribute group.
  [S390] Support for disconnected devices reappearing on another subchannel.
  [S390] subchannel lock conversion.
  [S390] Some preparations for the dynamic subchannel mapping patch.
  [S390] runtime switch for qdio performance statistics
  [S390] New DASD feature for ERP related logging
  [S390] add reset call handler to the ap bus.
  [S390] more workqueue fixes.
  [S390] workqueue fixes.
  [S390] uaccess_pt: add missing down_read() and convert to is_init().

35 files changed:
Documentation/s390/driver-model.txt
arch/s390/Kconfig
arch/s390/defconfig
arch/s390/kernel/setup.c
arch/s390/lib/uaccess_pt.c
arch/s390/mm/Makefile
arch/s390/mm/extmem.c
arch/s390/mm/init.c
arch/s390/mm/vmem.c [new file with mode: 0644]
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_int.h
drivers/s390/char/ctrlchar.c
drivers/s390/char/tape.h
drivers/s390/char/tape_34xx.c
drivers/s390/char/tape_3590.c
drivers/s390/char/tape_block.c
drivers/s390/char/tape_core.c
drivers/s390/cio/chsc.c
drivers/s390/cio/cio.c
drivers/s390/cio/cio.h
drivers/s390/cio/css.c
drivers/s390/cio/css.h
drivers/s390/cio/device.c
drivers/s390/cio/device.h
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/qdio.c
drivers/s390/cio/qdio.h
drivers/s390/crypto/ap_bus.c
include/asm-s390/dasd.h
include/asm-s390/page.h
include/asm-s390/pgalloc.h
include/asm-s390/pgtable.h

index 77bf450..e938c44 100644 (file)
@@ -18,11 +18,18 @@ devices/
           - 0.0.0002/
           - 0.1.0000/0.1.1234/
           ...
+          - defunct/
 
 In this example, device 0815 is accessed via subchannel 0 in subchannel set 0,
 device 4711 via subchannel 1 in subchannel set 0, and subchannel 2 is a non-I/O
 subchannel. Device 1234 is accessed via subchannel 0 in subchannel set 1.
 
+The subchannel named 'defunct' does not represent any real subchannel on the
+system; it is a pseudo subchannel where disconnnected ccw devices are moved to
+if they are displaced by another ccw device becoming operational on their
+former subchannel. The ccw devices will be moved again to a proper subchannel
+if they become operational again on that subchannel.
+
 You should address a ccw device via its bus id (e.g. 0.0.4711); the device can
 be found under bus/ccw/devices/.
 
index 45e47bf..ff69056 100644 (file)
@@ -241,8 +241,14 @@ config WARN_STACK_SIZE
          This allows you to specify the maximum frame size a function may
          have without the compiler complaining about it.
 
+config ARCH_POPULATES_NODE_MAP
+       def_bool y
+
 source "mm/Kconfig"
 
+config HOLES_IN_ZONE
+       def_bool y
+
 comment "I/O subsystem configuration"
 
 config MACHCHK_WARNING
@@ -266,14 +272,6 @@ config QDIO
 
          If unsure, say Y.
 
-config QDIO_PERF_STATS
-       bool "Performance statistics in /proc"
-       depends on QDIO
-       help
-         Say Y here to get performance statistics in /proc/qdio_perf
-
-         If unsure, say N.
-
 config QDIO_DEBUG
        bool "Extended debugging information"
        depends on QDIO
index 7cd51e7..a6ec919 100644 (file)
@@ -134,7 +134,6 @@ CONFIG_RESOURCES_64BIT=y
 #
 CONFIG_MACHCHK_WARNING=y
 CONFIG_QDIO=y
-# CONFIG_QDIO_PERF_STATS is not set
 # CONFIG_QDIO_DEBUG is not set
 
 #
index b928fec..49ef206 100644 (file)
@@ -64,9 +64,8 @@ unsigned int console_devno = -1;
 unsigned int console_irq = -1;
 unsigned long machine_flags = 0;
 
-struct mem_chunk memory_chunk[MEMORY_CHUNKS];
+struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
-unsigned long __initdata zholes_size[MAX_NR_ZONES];
 static unsigned long __initdata memory_end;
 
 /*
@@ -354,21 +353,6 @@ void machine_power_off(void)
  */
 void (*pm_power_off)(void) = machine_power_off;
 
-static void __init
-add_memory_hole(unsigned long start, unsigned long end)
-{
-       unsigned long dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT;
-
-       if (end <= dma_pfn)
-               zholes_size[ZONE_DMA] += end - start + 1;
-       else if (start > dma_pfn)
-               zholes_size[ZONE_NORMAL] += end - start + 1;
-       else {
-               zholes_size[ZONE_DMA] += dma_pfn - start + 1;
-               zholes_size[ZONE_NORMAL] += end - dma_pfn;
-       }
-}
-
 static int __init early_parse_mem(char *p)
 {
        memory_end = memparse(p, &p);
@@ -521,7 +505,6 @@ setup_memory(void)
 {
         unsigned long bootmap_size;
        unsigned long start_pfn, end_pfn, init_pfn;
-       unsigned long last_rw_end;
        int i;
 
        /*
@@ -577,39 +560,27 @@ setup_memory(void)
        /*
         * Register RAM areas with the bootmem allocator.
         */
-       last_rw_end = start_pfn;
 
        for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
-               unsigned long start_chunk, end_chunk;
+               unsigned long start_chunk, end_chunk, pfn;
 
                if (memory_chunk[i].type != CHUNK_READ_WRITE)
                        continue;
-               start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1);
-               start_chunk >>= PAGE_SHIFT;
-               end_chunk = (memory_chunk[i].addr + memory_chunk[i].size);
-               end_chunk >>= PAGE_SHIFT;
-               if (start_chunk < start_pfn)
-                       start_chunk = start_pfn;
-               if (end_chunk > end_pfn)
-                       end_chunk = end_pfn;
-               if (start_chunk < end_chunk) {
-                       /* Initialize storage key for RAM pages */
-                       for (init_pfn = start_chunk ; init_pfn < end_chunk;
-                            init_pfn++)
-                               page_set_storage_key(init_pfn << PAGE_SHIFT,
-                                                    PAGE_DEFAULT_KEY);
-                       free_bootmem(start_chunk << PAGE_SHIFT,
-                                    (end_chunk - start_chunk) << PAGE_SHIFT);
-                       if (last_rw_end < start_chunk)
-                               add_memory_hole(last_rw_end, start_chunk - 1);
-                       last_rw_end = end_chunk;
-               }
+               start_chunk = PFN_DOWN(memory_chunk[i].addr);
+               end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size) - 1;
+               end_chunk = min(end_chunk, end_pfn);
+               if (start_chunk >= end_chunk)
+                       continue;
+               add_active_range(0, start_chunk, end_chunk);
+               pfn = max(start_chunk, start_pfn);
+               for (; pfn <= end_chunk; pfn++)
+                       page_set_storage_key(PFN_PHYS(pfn), PAGE_DEFAULT_KEY);
        }
 
        psw_set_key(PAGE_DEFAULT_KEY);
 
-       if (last_rw_end < end_pfn - 1)
-               add_memory_hole(last_rw_end, end_pfn - 1);
+       free_bootmem_with_active_regions(0, max_pfn);
+       reserve_bootmem(0, PFN_PHYS(start_pfn));
 
        /*
         * Reserve the bootmem bitmap itself as well. We do this in two
index 8741bdc..633249c 100644 (file)
@@ -8,8 +8,8 @@
  */
 
 #include <linux/errno.h>
-#include <asm/uaccess.h>
 #include <linux/mm.h>
+#include <asm/uaccess.h>
 #include <asm/futex.h>
 
 static inline int __handle_fault(struct mm_struct *mm, unsigned long address,
@@ -60,8 +60,9 @@ out:
 
 out_of_memory:
        up_read(&mm->mmap_sem);
-       if (current->pid == 1) {
+       if (is_init(current)) {
                yield();
+               down_read(&mm->mmap_sem);
                goto survive;
        }
        printk("VM: killing process %s\n", current->comm);
index aa9a42b..8e09db1 100644 (file)
@@ -2,6 +2,6 @@
 # Makefile for the linux s390-specific parts of the memory manager.
 #
 
-obj-y   := init.o fault.o ioremap.o extmem.o mmap.o
+obj-y   := init.o fault.o ioremap.o extmem.o mmap.o vmem.o
 obj-$(CONFIG_CMM) += cmm.o
 
index 9e9bc48..775bf19 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/bootmem.h>
 #include <linux/ctype.h>
 #include <asm/page.h>
+#include <asm/pgtable.h>
 #include <asm/ebcdic.h>
 #include <asm/errno.h>
 #include <asm/extmem.h>
@@ -238,65 +239,6 @@ query_segment_type (struct dcss_segment *seg)
 }
 
 /*
- * check if the given segment collides with guest storage.
- * returns 1 if this is the case, 0 if no collision was found
- */
-static int
-segment_overlaps_storage(struct dcss_segment *seg)
-{
-       int i;
-
-       for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
-               if (memory_chunk[i].type != CHUNK_READ_WRITE)
-                       continue;
-               if ((memory_chunk[i].addr >> 20) > (seg->end >> 20))
-                       continue;
-               if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20)
-                               < (seg->start_addr >> 20))
-                       continue;
-               return 1;
-       }
-       return 0;
-}
-
-/*
- * check if segment collides with other segments that are currently loaded
- * returns 1 if this is the case, 0 if no collision was found
- */
-static int
-segment_overlaps_others (struct dcss_segment *seg)
-{
-       struct list_head *l;
-       struct dcss_segment *tmp;
-
-       BUG_ON(!mutex_is_locked(&dcss_lock));
-       list_for_each(l, &dcss_list) {
-               tmp = list_entry(l, struct dcss_segment, list);
-               if ((tmp->start_addr >> 20) > (seg->end >> 20))
-                       continue;
-               if ((tmp->end >> 20) < (seg->start_addr >> 20))
-                       continue;
-               if (seg == tmp)
-                       continue;
-               return 1;
-       }
-       return 0;
-}
-
-/*
- * check if segment exceeds the kernel mapping range (detected or set via mem=)
- * returns 1 if this is the case, 0 if segment fits into the range
- */
-static inline int
-segment_exceeds_range (struct dcss_segment *seg)
-{
-       int seg_last_pfn = (seg->end) >> PAGE_SHIFT;
-       if (seg_last_pfn > max_pfn)
-               return 1;
-       return 0;
-}
-
-/*
  * get info about a segment
  * possible return values:
  * -ENOSYS  : we are not running on VM
@@ -341,24 +283,26 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
        rc = query_segment_type (seg);
        if (rc < 0)
                goto out_free;
-       if (segment_exceeds_range(seg)) {
-               PRINT_WARN ("segment_load: not loading segment %s - exceeds"
-                               " kernel mapping range\n",name);
-               rc = -ERANGE;
+
+       rc = add_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+
+       switch (rc) {
+       case 0:
+               break;
+       case -ENOSPC:
+               PRINT_WARN("segment_load: not loading segment %s - overlaps "
+                          "storage/segment\n", name);
                goto out_free;
-       }
-       if (segment_overlaps_storage(seg)) {
-               PRINT_WARN ("segment_load: not loading segment %s - overlaps"
-                               " storage\n",name);
-               rc = -ENOSPC;
+       case -ERANGE:
+               PRINT_WARN("segment_load: not loading segment %s - exceeds "
+                          "kernel mapping range\n", name);
                goto out_free;
-       }
-       if (segment_overlaps_others(seg)) {
-               PRINT_WARN ("segment_load: not loading segment %s - overlaps"
-                               " other segments\n",name);
-               rc = -EBUSY;
+       default:
+               PRINT_WARN("segment_load: not loading segment %s (rc: %d)\n",
+                          name, rc);
                goto out_free;
        }
+
        if (do_nonshared)
                dcss_command = DCSS_LOADNSR;
        else
@@ -372,7 +316,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
                rc = dcss_diag_translate_rc (seg->end);
                dcss_diag(DCSS_PURGESEG, seg->dcss_name,
                                &seg->start_addr, &seg->end);
-               goto out_free;
+               goto out_shared;
        }
        seg->do_nonshared = do_nonshared;
        atomic_set(&seg->ref_count, 1);
@@ -391,6 +335,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
                                (void*)seg->start_addr, (void*)seg->end,
                                segtype_string[seg->vm_segtype]);
        goto out;
+ out_shared:
+       remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
  out_free:
        kfree(seg);
  out:
@@ -530,12 +476,12 @@ segment_unload(char *name)
                                "please report to linux390@de.ibm.com\n",name);
                goto out_unlock;
        }
-       if (atomic_dec_return(&seg->ref_count) == 0) {
-               list_del(&seg->list);
-               dcss_diag(DCSS_PURGESEG, seg->dcss_name,
-                         &dummy, &dummy);
-               kfree(seg);
-       }
+       if (atomic_dec_return(&seg->ref_count) != 0)
+               goto out_unlock;
+       remove_shared_memory(seg->start_addr, seg->end - seg->start_addr + 1);
+       list_del(&seg->list);
+       dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
+       kfree(seg);
 out_unlock:
        mutex_unlock(&dcss_lock);
 }
index e1881c3..4bb21be 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/pagemap.h>
 #include <linux/bootmem.h>
 #include <linux/pfn.h>
+#include <linux/poison.h>
 
 #include <asm/processor.h>
 #include <asm/system.h>
@@ -69,6 +70,8 @@ void show_mem(void)
         printk("Free swap:       %6ldkB\n", nr_swap_pages<<(PAGE_SHIFT-10));
         i = max_mapnr;
         while (i-- > 0) {
+               if (!pfn_valid(i))
+                       continue;
                page = pfn_to_page(i);
                 total++;
                if (PageReserved(page))
@@ -84,150 +87,52 @@ void show_mem(void)
         printk("%d pages swap cached\n",cached);
 }
 
-extern unsigned long __initdata zholes_size[];
-/*
- * paging_init() sets up the page tables
- */
-
-#ifndef CONFIG_64BIT
-void __init paging_init(void)
+static void __init setup_ro_region(void)
 {
-        pgd_t * pg_dir;
-        pte_t * pg_table;
-        pte_t   pte;
-       int     i;
-        unsigned long tmp;
-        unsigned long pfn = 0;
-        unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
-        static const int ssm_mask = 0x04000000L;
-       unsigned long ro_start_pfn, ro_end_pfn;
-       unsigned long zones_size[MAX_NR_ZONES];
-
-       ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata);
-       ro_end_pfn = PFN_UP((unsigned long)&__end_rodata);
-
-       memset(zones_size, 0, sizeof(zones_size));
-       zones_size[ZONE_DMA] = max_low_pfn;
-       free_area_init_node(0, &contig_page_data, zones_size,
-                           __pa(PAGE_OFFSET) >> PAGE_SHIFT,
-                           zholes_size);
-
-       /* unmap whole virtual address space */
-       
-        pg_dir = swapper_pg_dir;
-
-       for (i = 0; i < PTRS_PER_PGD; i++)
-               pmd_clear((pmd_t *) pg_dir++);
-               
-       /*
-        * map whole physical memory to virtual memory (identity mapping) 
-        */
-
-        pg_dir = swapper_pg_dir;
-
-        while (pfn < max_low_pfn) {
-                /*
-                 * pg_table is physical at this point
-                 */
-               pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
-
-               pmd_populate_kernel(&init_mm, (pmd_t *) pg_dir, pg_table);
-                pg_dir++;
-
-                for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
-                       if (pfn >= ro_start_pfn && pfn < ro_end_pfn)
-                               pte = pfn_pte(pfn, __pgprot(_PAGE_RO));
-                       else
-                               pte = pfn_pte(pfn, PAGE_KERNEL);
-                        if (pfn >= max_low_pfn)
-                               pte_val(pte) = _PAGE_TYPE_EMPTY;
-                       set_pte(pg_table, pte);
-                        pfn++;
-                }
-        }
-
-       S390_lowcore.kernel_asce = pgdir_k;
-
-        /* enable virtual mapping in kernel mode */
-       __ctl_load(pgdir_k, 1, 1);
-       __ctl_load(pgdir_k, 7, 7);
-       __ctl_load(pgdir_k, 13, 13);
-       __raw_local_irq_ssm(ssm_mask);
-
-        local_flush_tlb();
+       pgd_t *pgd;
+       pmd_t *pmd;
+       pte_t *pte;
+       pte_t new_pte;
+       unsigned long address, end;
+
+       address = ((unsigned long)&__start_rodata) & PAGE_MASK;
+       end = PFN_ALIGN((unsigned long)&__end_rodata);
+
+       for (; address < end; address += PAGE_SIZE) {
+               pgd = pgd_offset_k(address);
+               pmd = pmd_offset(pgd, address);
+               pte = pte_offset_kernel(pmd, address);
+               new_pte = mk_pte_phys(address, __pgprot(_PAGE_RO));
+               set_pte(pte, new_pte);
+       }
 }
 
-#else /* CONFIG_64BIT */
+extern void vmem_map_init(void);
 
+/*
+ * paging_init() sets up the page tables
+ */
 void __init paging_init(void)
 {
-        pgd_t * pg_dir;
-       pmd_t * pm_dir;
-        pte_t * pt_dir;
-        pte_t   pte;
-       int     i,j,k;
-        unsigned long pfn = 0;
-        unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) |
-          _KERN_REGION_TABLE;
+       pgd_t *pg_dir;
+       int i;
+       unsigned long pgdir_k;
        static const int ssm_mask = 0x04000000L;
-       unsigned long zones_size[MAX_NR_ZONES];
-       unsigned long dma_pfn, high_pfn;
-       unsigned long ro_start_pfn, ro_end_pfn;
-
-       memset(zones_size, 0, sizeof(zones_size));
-       dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT;
-       high_pfn = max_low_pfn;
-       ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata);
-       ro_end_pfn = PFN_UP((unsigned long)&__end_rodata);
-
-       if (dma_pfn > high_pfn)
-               zones_size[ZONE_DMA] = high_pfn;
-       else {
-               zones_size[ZONE_DMA] = dma_pfn;
-               zones_size[ZONE_NORMAL] = high_pfn - dma_pfn;
-       }
-
-       /* Initialize mem_map[].  */
-       free_area_init_node(0, &contig_page_data, zones_size,
-                           __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size);
+       unsigned long max_zone_pfns[MAX_NR_ZONES];
 
-       /*
-        * map whole physical memory to virtual memory (identity mapping) 
-        */
-
-        pg_dir = swapper_pg_dir;
-       
-        for (i = 0 ; i < PTRS_PER_PGD ; i++,pg_dir++) {
+       pg_dir = swapper_pg_dir;
        
-                if (pfn >= max_low_pfn) {
-                        pgd_clear(pg_dir);
-                        continue;
-                }          
-        
-               pm_dir = (pmd_t *) alloc_bootmem_pages(PAGE_SIZE * 4);
-                pgd_populate(&init_mm, pg_dir, pm_dir);
-
-                for (j = 0 ; j < PTRS_PER_PMD ; j++,pm_dir++) {
-                        if (pfn >= max_low_pfn) {
-                                pmd_clear(pm_dir);
-                                continue; 
-                        }          
-                        
-                       pt_dir = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
-                        pmd_populate_kernel(&init_mm, pm_dir, pt_dir);
-       
-                        for (k = 0 ; k < PTRS_PER_PTE ; k++,pt_dir++) {
-                               if (pfn >= ro_start_pfn && pfn < ro_end_pfn)
-                                       pte = pfn_pte(pfn, __pgprot(_PAGE_RO));
-                               else
-                                       pte = pfn_pte(pfn, PAGE_KERNEL);
-                               if (pfn >= max_low_pfn)
-                                       pte_val(pte) = _PAGE_TYPE_EMPTY;
-                                set_pte(pt_dir, pte);
-                                pfn++;
-                        }
-                }
-        }
+#ifdef CONFIG_64BIT
+       pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERN_REGION_TABLE;
+       for (i = 0; i < PTRS_PER_PGD; i++)
+               pgd_clear(pg_dir + i);
+#else
+       pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
+       for (i = 0; i < PTRS_PER_PGD; i++)
+               pmd_clear((pmd_t *)(pg_dir + i));
+#endif
+       vmem_map_init();
+       setup_ro_region();
 
        S390_lowcore.kernel_asce = pgdir_k;
 
@@ -237,9 +142,11 @@ void __init paging_init(void)
        __ctl_load(pgdir_k, 13, 13);
        __raw_local_irq_ssm(ssm_mask);
 
-        local_flush_tlb();
+       memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
+       max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS);
+       max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+       free_area_init_nodes(max_zone_pfns);
 }
-#endif /* CONFIG_64BIT */
 
 void __init mem_init(void)
 {
@@ -269,6 +176,8 @@ void __init mem_init(void)
        printk("Write protected kernel read-only data: %#lx - %#lx\n",
               (unsigned long)&__start_rodata,
               PFN_ALIGN((unsigned long)&__end_rodata) - 1);
+       printk("Virtual memmap size: %ldk\n",
+              (max_pfn * sizeof(struct page)) >> 10);
 }
 
 void free_initmem(void)
@@ -279,6 +188,7 @@ void free_initmem(void)
         for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
                ClearPageReserved(virt_to_page(addr));
                init_page_count(virt_to_page(addr));
+               memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
                free_page(addr);
                totalram_pages++;
         }
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
new file mode 100644 (file)
index 0000000..7f2944d
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ *  arch/s390/mm/vmem.c
+ *
+ *    Copyright IBM Corp. 2006
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/bootmem.h>
+#include <linux/pfn.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/setup.h>
+#include <asm/tlbflush.h>
+
+unsigned long vmalloc_end;
+EXPORT_SYMBOL(vmalloc_end);
+
+static struct page *vmem_map;
+static DEFINE_MUTEX(vmem_mutex);
+
+struct memory_segment {
+       struct list_head list;
+       unsigned long start;
+       unsigned long size;
+};
+
+static LIST_HEAD(mem_segs);
+
+void memmap_init(unsigned long size, int nid, unsigned long zone,
+                unsigned long start_pfn)
+{
+       struct page *start, *end;
+       struct page *map_start, *map_end;
+       int i;
+
+       start = pfn_to_page(start_pfn);
+       end = start + size;
+
+       for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+               unsigned long cstart, cend;
+
+               cstart = PFN_DOWN(memory_chunk[i].addr);
+               cend = cstart + PFN_DOWN(memory_chunk[i].size);
+
+               map_start = mem_map + cstart;
+               map_end = mem_map + cend;
+
+               if (map_start < start)
+                       map_start = start;
+               if (map_end > end)
+                       map_end = end;
+
+               map_start -= ((unsigned long) map_start & (PAGE_SIZE - 1))
+                       / sizeof(struct page);
+               map_end += ((PFN_ALIGN((unsigned long) map_end)
+                            - (unsigned long) map_end)
+                           / sizeof(struct page));
+
+               if (map_start < map_end)
+                       memmap_init_zone((unsigned long)(map_end - map_start),
+                                        nid, zone, page_to_pfn(map_start));
+       }
+}
+
+static inline void *vmem_alloc_pages(unsigned int order)
+{
+       if (slab_is_available())
+               return (void *)__get_free_pages(GFP_KERNEL, order);
+       return alloc_bootmem_pages((1 << order) * PAGE_SIZE);
+}
+
+static inline pmd_t *vmem_pmd_alloc(void)
+{
+       pmd_t *pmd;
+       int i;
+
+       pmd = vmem_alloc_pages(PMD_ALLOC_ORDER);
+       if (!pmd)
+               return NULL;
+       for (i = 0; i < PTRS_PER_PMD; i++)
+               pmd_clear(pmd + i);
+       return pmd;
+}
+
+static inline pte_t *vmem_pte_alloc(void)
+{
+       pte_t *pte;
+       pte_t empty_pte;
+       int i;
+
+       pte = vmem_alloc_pages(PTE_ALLOC_ORDER);
+       if (!pte)
+               return NULL;
+       pte_val(empty_pte) = _PAGE_TYPE_EMPTY;
+       for (i = 0; i < PTRS_PER_PTE; i++)
+               set_pte(pte + i, empty_pte);
+       return pte;
+}
+
+/*
+ * Add a physical memory range to the 1:1 mapping.
+ */
+static int vmem_add_range(unsigned long start, unsigned long size)
+{
+       unsigned long address;
+       pgd_t *pg_dir;
+       pmd_t *pm_dir;
+       pte_t *pt_dir;
+       pte_t  pte;
+       int ret = -ENOMEM;
+
+       for (address = start; address < start + size; address += PAGE_SIZE) {
+               pg_dir = pgd_offset_k(address);
+               if (pgd_none(*pg_dir)) {
+                       pm_dir = vmem_pmd_alloc();
+                       if (!pm_dir)
+                               goto out;
+                       pgd_populate(&init_mm, pg_dir, pm_dir);
+               }
+
+               pm_dir = pmd_offset(pg_dir, address);
+               if (pmd_none(*pm_dir)) {
+                       pt_dir = vmem_pte_alloc();
+                       if (!pt_dir)
+                               goto out;
+                       pmd_populate_kernel(&init_mm, pm_dir, pt_dir);
+               }
+
+               pt_dir = pte_offset_kernel(pm_dir, address);
+               pte = pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL);
+               set_pte(pt_dir, pte);
+       }
+       ret = 0;
+out:
+       flush_tlb_kernel_range(start, start + size);
+       return ret;
+}
+
+/*
+ * Remove a physical memory range from the 1:1 mapping.
+ * Currently only invalidates page table entries.
+ */
+static void vmem_remove_range(unsigned long start, unsigned long size)
+{
+       unsigned long address;
+       pgd_t *pg_dir;
+       pmd_t *pm_dir;
+       pte_t *pt_dir;
+       pte_t  pte;
+
+       pte_val(pte) = _PAGE_TYPE_EMPTY;
+       for (address = start; address < start + size; address += PAGE_SIZE) {
+               pg_dir = pgd_offset_k(address);
+               if (pgd_none(*pg_dir))
+                       continue;
+               pm_dir = pmd_offset(pg_dir, address);
+               if (pmd_none(*pm_dir))
+                       continue;
+               pt_dir = pte_offset_kernel(pm_dir, address);
+               set_pte(pt_dir, pte);
+       }
+       flush_tlb_kernel_range(start, start + size);
+}
+
+/*
+ * Add a backed mem_map array to the virtual mem_map array.
+ */
+static int vmem_add_mem_map(unsigned long start, unsigned long size)
+{
+       unsigned long address, start_addr, end_addr;
+       struct page *map_start, *map_end;
+       pgd_t *pg_dir;
+       pmd_t *pm_dir;
+       pte_t *pt_dir;
+       pte_t  pte;
+       int ret = -ENOMEM;
+
+       map_start = vmem_map + PFN_DOWN(start);
+       map_end = vmem_map + PFN_DOWN(start + size);
+
+       start_addr = (unsigned long) map_start & PAGE_MASK;
+       end_addr = PFN_ALIGN((unsigned long) map_end);
+
+       for (address = start_addr; address < end_addr; address += PAGE_SIZE) {
+               pg_dir = pgd_offset_k(address);
+               if (pgd_none(*pg_dir)) {
+                       pm_dir = vmem_pmd_alloc();
+                       if (!pm_dir)
+                               goto out;
+                       pgd_populate(&init_mm, pg_dir, pm_dir);
+               }
+
+               pm_dir = pmd_offset(pg_dir, address);
+               if (pmd_none(*pm_dir)) {
+                       pt_dir = vmem_pte_alloc();
+                       if (!pt_dir)
+                               goto out;
+                       pmd_populate_kernel(&init_mm, pm_dir, pt_dir);
+               }
+
+               pt_dir = pte_offset_kernel(pm_dir, address);
+               if (pte_none(*pt_dir)) {
+                       unsigned long new_page;
+
+                       new_page =__pa(vmem_alloc_pages(0));
+                       if (!new_page)
+                               goto out;
+                       pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL);
+                       set_pte(pt_dir, pte);
+               }
+       }
+       ret = 0;
+out:
+       flush_tlb_kernel_range(start_addr, end_addr);
+       return ret;
+}
+
+static int vmem_add_mem(unsigned long start, unsigned long size)
+{
+       int ret;
+
+       ret = vmem_add_range(start, size);
+       if (ret)
+               return ret;
+       return vmem_add_mem_map(start, size);
+}
+
+/*
+ * Add memory segment to the segment list if it doesn't overlap with
+ * an already present segment.
+ */
+static int insert_memory_segment(struct memory_segment *seg)
+{
+       struct memory_segment *tmp;
+
+       if (PFN_DOWN(seg->start + seg->size) > max_pfn ||
+           seg->start + seg->size < seg->start)
+               return -ERANGE;
+
+       list_for_each_entry(tmp, &mem_segs, list) {
+               if (seg->start >= tmp->start + tmp->size)
+                       continue;
+               if (seg->start + seg->size <= tmp->start)
+                       continue;
+               return -ENOSPC;
+       }
+       list_add(&seg->list, &mem_segs);
+       return 0;
+}
+
+/*
+ * Remove memory segment from the segment list.
+ */
+static void remove_memory_segment(struct memory_segment *seg)
+{
+       list_del(&seg->list);
+}
+
+static void __remove_shared_memory(struct memory_segment *seg)
+{
+       remove_memory_segment(seg);
+       vmem_remove_range(seg->start, seg->size);
+}
+
+int remove_shared_memory(unsigned long start, unsigned long size)
+{
+       struct memory_segment *seg;
+       int ret;
+
+       mutex_lock(&vmem_mutex);
+
+       ret = -ENOENT;
+       list_for_each_entry(seg, &mem_segs, list) {
+               if (seg->start == start && seg->size == size)
+                       break;
+       }
+
+       if (seg->start != start || seg->size != size)
+               goto out;
+
+       ret = 0;
+       __remove_shared_memory(seg);
+       kfree(seg);
+out:
+       mutex_unlock(&vmem_mutex);
+       return ret;
+}
+
+int add_shared_memory(unsigned long start, unsigned long size)
+{
+       struct memory_segment *seg;
+       struct page *page;
+       unsigned long pfn, num_pfn, end_pfn;
+       int ret;
+
+       mutex_lock(&vmem_mutex);
+       ret = -ENOMEM;
+       seg = kzalloc(sizeof(*seg), GFP_KERNEL);
+       if (!seg)
+               goto out;
+       seg->start = start;
+       seg->size = size;
+
+       ret = insert_memory_segment(seg);
+       if (ret)
+               goto out_free;
+
+       ret = vmem_add_mem(start, size);
+       if (ret)
+               goto out_remove;
+
+       pfn = PFN_DOWN(start);
+       num_pfn = PFN_DOWN(size);
+       end_pfn = pfn + num_pfn;
+
+       page = pfn_to_page(pfn);
+       memset(page, 0, num_pfn * sizeof(struct page));
+
+       for (; pfn < end_pfn; pfn++) {
+               page = pfn_to_page(pfn);
+               init_page_count(page);
+               reset_page_mapcount(page);
+               SetPageReserved(page);
+               INIT_LIST_HEAD(&page->lru);
+       }
+       goto out;
+
+out_remove:
+       __remove_shared_memory(seg);
+out_free:
+       kfree(seg);
+out:
+       mutex_unlock(&vmem_mutex);
+       return ret;
+}
+
+/*
+ * map whole physical memory to virtual memory (identity mapping)
+ */
+void __init vmem_map_init(void)
+{
+       unsigned long map_size;
+       int i;
+
+       map_size = ALIGN(max_low_pfn, MAX_ORDER_NR_PAGES) * sizeof(struct page);
+       vmalloc_end = PFN_ALIGN(VMALLOC_END_INIT) - PFN_ALIGN(map_size);
+       vmem_map = (struct page *) vmalloc_end;
+       NODE_DATA(0)->node_mem_map = vmem_map;
+
+       for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++)
+               vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size);
+}
+
+/*
+ * Convert memory chunk array to a memory segment list so there is a single
+ * list that contains both r/w memory and shared memory segments.
+ */
+static int __init vmem_convert_memory_chunk(void)
+{
+       struct memory_segment *seg;
+       int i;
+
+       mutex_lock(&vmem_mutex);
+       for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+               if (!memory_chunk[i].size)
+                       continue;
+               seg = kzalloc(sizeof(*seg), GFP_KERNEL);
+               if (!seg)
+                       panic("Out of memory...\n");
+               seg->start = memory_chunk[i].addr;
+               seg->size = memory_chunk[i].size;
+               insert_memory_segment(seg);
+       }
+       mutex_unlock(&vmem_mutex);
+       return 0;
+}
+
+core_initcall(vmem_convert_memory_chunk);
index 2af2d9b..492b68b 100644 (file)
@@ -1050,10 +1050,10 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
                }
        } else {                /* error */
                memcpy(&cqr->irb, irb, sizeof (struct irb));
-#ifdef ERP_DEBUG
-               /* dump sense data */
-               dasd_log_sense(cqr, irb);
-#endif
+               if (device->features & DASD_FEATURE_ERPLOG) {
+                       /* dump sense data */
+                       dasd_log_sense(cqr, irb);
+               }
                switch (era) {
                case dasd_era_fatal:
                        cqr->status = DASD_CQR_FAILED;
index 669805d..4d01040 100644 (file)
@@ -2641,14 +2641,12 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
        struct dasd_ccw_req *erp = NULL;
        struct dasd_device *device = cqr->device;
        __u32 cpa = cqr->irb.scsw.cpa;
+       struct dasd_ccw_req *temp_erp = NULL;
 
-#ifdef ERP_DEBUG
-       /* print current erp_chain */
-       DEV_MESSAGE(KERN_ERR, device, "%s",
-                   "ERP chain at BEGINNING of ERP-ACTION");
-       {
-               struct dasd_ccw_req *temp_erp = NULL;
-
+       if (device->features & DASD_FEATURE_ERPLOG) {
+               /* print current erp_chain */
+               DEV_MESSAGE(KERN_ERR, device, "%s",
+                           "ERP chain at BEGINNING of ERP-ACTION");
                for (temp_erp = cqr;
                     temp_erp != NULL; temp_erp = temp_erp->refers) {
 
@@ -2658,7 +2656,6 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
                                    temp_erp->refers);
                }
        }
-#endif                         /* ERP_DEBUG */
 
        /* double-check if current erp/cqr was successfull */
        if ((cqr->irb.scsw.cstat == 0x00) &&
@@ -2695,11 +2692,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
                erp = dasd_3990_erp_handle_match_erp(cqr, erp);
        }
 
-#ifdef ERP_DEBUG
-       /* print current erp_chain */
-       DEV_MESSAGE(KERN_ERR, device, "%s", "ERP chain at END of ERP-ACTION");
-       {
-               struct dasd_ccw_req *temp_erp = NULL;
+       if (device->features & DASD_FEATURE_ERPLOG) {
+               /* print current erp_chain */
+               DEV_MESSAGE(KERN_ERR, device, "%s",
+                           "ERP chain at END of ERP-ACTION");
                for (temp_erp = erp;
                     temp_erp != NULL; temp_erp = temp_erp->refers) {
 
@@ -2709,7 +2705,6 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
                                    temp_erp->refers);
                }
        }
-#endif                         /* ERP_DEBUG */
 
        if (erp->status == DASD_CQR_FAILED)
                dasd_log_ccw(erp, 1, cpa);
index cf28ccc..5943266 100644 (file)
@@ -202,6 +202,8 @@ dasd_feature_list(char *str, char **endp)
                        features |= DASD_FEATURE_READONLY;
                else if (len == 4 && !strncmp(str, "diag", 4))
                        features |= DASD_FEATURE_USEDIAG;
+               else if (len == 6 && !strncmp(str, "erplog", 6))
+                       features |= DASD_FEATURE_ERPLOG;
                else {
                        MESSAGE(KERN_WARNING,
                                "unsupported feature: %*s, "
@@ -709,6 +711,52 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
 }
 
 static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
+/*
+ * erplog controls the logging of ERP related data
+ * (e.g. failing channel programs).
+ */
+static ssize_t
+dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dasd_devmap *devmap;
+       int erplog;
+
+       devmap = dasd_find_busid(dev->bus_id);
+       if (!IS_ERR(devmap))
+               erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
+       else
+               erplog = (DASD_FEATURE_DEFAULT & DASD_FEATURE_ERPLOG) != 0;
+       return snprintf(buf, PAGE_SIZE, erplog ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_erplog_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct dasd_devmap *devmap;
+       int val;
+       char *endp;
+
+       devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
+       if (IS_ERR(devmap))
+               return PTR_ERR(devmap);
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
+       spin_lock(&dasd_devmap_lock);
+       if (val)
+               devmap->features |= DASD_FEATURE_ERPLOG;
+       else
+               devmap->features &= ~DASD_FEATURE_ERPLOG;
+       if (devmap->device)
+               devmap->device->features = devmap->features;
+       spin_unlock(&dasd_devmap_lock);
+       return count;
+}
+
+static DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store);
 
 /*
  * use_diag controls whether the driver should use diag rather than ssch
@@ -896,6 +944,7 @@ static struct attribute * dasd_attrs[] = {
        &dev_attr_uid.attr,
        &dev_attr_use_diag.attr,
        &dev_attr_eer_enabled.attr,
+       &dev_attr_erplog.attr,
        NULL,
 };
 
index dc5dd50..fb725e3 100644 (file)
 
 #ifdef __KERNEL__
 
-/* erp debugging in dasd.c and dasd_3990_erp.c */
-#define ERP_DEBUG
-
-
 /* we keep old device allocation scheme; IOW, minors are still in 0..255 */
 #define DASD_PER_MAJOR (1U << (MINORBITS - DASD_PARTN_BITS))
 #define DASD_PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
index 49e9628..c6cbcb3 100644 (file)
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static int ctrlchar_sysrq_key;
+static struct tty_struct *sysrq_tty;
 
 static void
-ctrlchar_handle_sysrq(void *tty)
+ctrlchar_handle_sysrq(struct work_struct *work)
 {
-       handle_sysrq(ctrlchar_sysrq_key, (struct tty_struct *) tty);
+       handle_sysrq(ctrlchar_sysrq_key, sysrq_tty);
 }
 
-static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq, NULL);
+static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq);
 #endif
 
 
@@ -53,7 +54,7 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty)
        /* racy */
        if (len == 3 && buf[1] == '-') {
                ctrlchar_sysrq_key = buf[2];
-               ctrlchar_work.data = tty;
+               sysrq_tty = tty;
                schedule_work(&ctrlchar_work);
                return CTRLCHAR_SYSRQ;
        }
index 1f4c899..c9f1c4c 100644 (file)
@@ -179,6 +179,7 @@ struct tape_char_data {
 /* Block Frontend Data */
 struct tape_blk_data
 {
+       struct tape_device *    device;
        /* Block device request queue. */
        request_queue_t *       request_queue;
        spinlock_t              request_queue_lock;
@@ -240,7 +241,7 @@ struct tape_device {
 #endif
 
        /* Function to start or stop the next request later. */
-       struct work_struct              tape_dnr;
+       struct delayed_work             tape_dnr;
 };
 
 /* Externals from tape_core.c */
index 7b95dab..e765875 100644 (file)
@@ -95,6 +95,12 @@ tape_34xx_medium_sense(struct tape_device *device)
        return rc;
 }
 
+struct tape_34xx_work {
+       struct tape_device      *device;
+       enum tape_op             op;
+       struct work_struct       work;
+};
+
 /*
  * These functions are currently used only to schedule a medium_sense for
  * later execution. This is because we get an interrupt whenever a medium
@@ -103,13 +109,10 @@ tape_34xx_medium_sense(struct tape_device *device)
  * interrupt handler.
  */
 static void
-tape_34xx_work_handler(void *data)
+tape_34xx_work_handler(struct work_struct *work)
 {
-       struct {
-               struct tape_device      *device;
-               enum tape_op             op;
-               struct work_struct       work;
-       } *p = data;
+       struct tape_34xx_work *p =
+               container_of(work, struct tape_34xx_work, work);
 
        switch(p->op) {
                case TO_MSEN:
@@ -126,17 +129,13 @@ tape_34xx_work_handler(void *data)
 static int
 tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
 {
-       struct {
-               struct tape_device      *device;
-               enum tape_op             op;
-               struct work_struct       work;
-       } *p;
+       struct tape_34xx_work *p;
 
        if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
                return -ENOMEM;
 
        memset(p, 0, sizeof(*p));
-       INIT_WORK(&p->work, tape_34xx_work_handler, p);
+       INIT_WORK(&p->work, tape_34xx_work_handler);
 
        p->device = tape_get_device_reference(device);
        p->op     = op;
index 928cbef..9df912f 100644 (file)
@@ -236,9 +236,10 @@ struct work_handler_data {
 };
 
 static void
-tape_3590_work_handler(void *data)
+tape_3590_work_handler(struct work_struct *work)
 {
-       struct work_handler_data *p = data;
+       struct work_handler_data *p =
+               container_of(work, struct work_handler_data, work);
 
        switch (p->op) {
        case TO_MSEN:
@@ -263,7 +264,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
        if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
                return -ENOMEM;
 
-       INIT_WORK(&p->work, tape_3590_work_handler, p);
+       INIT_WORK(&p->work, tape_3590_work_handler);
 
        p->device = tape_get_device_reference(device);
        p->op = op;
index 3225fcd..c8a89b3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/blkdev.h>
 #include <linux/interrupt.h>
 #include <linux/buffer_head.h>
+#include <linux/kernel.h>
 
 #include <asm/debug.h>
 
@@ -143,7 +144,8 @@ tapeblock_start_request(struct tape_device *device, struct request *req)
  * queue.
  */
 static void
-tapeblock_requeue(void *data) {
+tapeblock_requeue(struct work_struct *work) {
+       struct tape_blk_data *  blkdat;
        struct tape_device *    device;
        request_queue_t *       queue;
        int                     nr_queued;
@@ -151,7 +153,8 @@ tapeblock_requeue(void *data) {
        struct list_head *      l;
        int                     rc;
 
-       device = (struct tape_device *) data;
+       blkdat = container_of(work, struct tape_blk_data, requeue_task);
+       device = blkdat->device;
        if (!device)
                return;
 
@@ -212,6 +215,7 @@ tapeblock_setup_device(struct tape_device * device)
        int                     rc;
 
        blkdat = &device->blk_data;
+       blkdat->device = device;
        spin_lock_init(&blkdat->request_queue_lock);
        atomic_set(&blkdat->requeue_scheduled, 0);
 
@@ -255,8 +259,8 @@ tapeblock_setup_device(struct tape_device * device)
 
        add_disk(disk);
 
-       INIT_WORK(&blkdat->requeue_task, tapeblock_requeue,
-               tape_get_device_reference(device));
+       tape_get_device_reference(device);
+       INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
 
        return 0;
 
@@ -271,7 +275,7 @@ void
 tapeblock_cleanup_device(struct tape_device *device)
 {
        flush_scheduled_work();
-       device->blk_data.requeue_task.data = tape_put_device(device);
+       tape_put_device(device);
 
        if (!device->blk_data.disk) {
                PRINT_ERR("(%s): No gendisk to clean up!\n",
index 2826aed..c6c2e91 100644 (file)
@@ -28,7 +28,7 @@
 #define PRINTK_HEADER "TAPE_CORE: "
 
 static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *);
-static void tape_delayed_next_request(void * data);
+static void tape_delayed_next_request(struct work_struct *);
 
 /*
  * One list to contain all tape devices of all disciplines, so
@@ -272,7 +272,7 @@ __tape_cancel_io(struct tape_device *device, struct tape_request *request)
                                return 0;
                        case -EBUSY:
                                request->status = TAPE_REQUEST_CANCEL;
-                               schedule_work(&device->tape_dnr);
+                               schedule_delayed_work(&device->tape_dnr, 0);
                                return 0;
                        case -ENODEV:
                                DBF_EXCEPTION(2, "device gone, retry\n");
@@ -470,7 +470,7 @@ tape_alloc_device(void)
        *device->modeset_byte = 0;
        device->first_minor = -1;
        atomic_set(&device->ref_count, 1);
-       INIT_WORK(&device->tape_dnr, tape_delayed_next_request, device);
+       INIT_DELAYED_WORK(&device->tape_dnr, tape_delayed_next_request);
 
        return device;
 }
@@ -724,7 +724,7 @@ __tape_start_io(struct tape_device *device, struct tape_request *request)
        } else if (rc == -EBUSY) {
                /* The common I/O subsystem is currently busy. Retry later. */
                request->status = TAPE_REQUEST_QUEUED;
-               schedule_work(&device->tape_dnr);
+               schedule_delayed_work(&device->tape_dnr, 0);
                rc = 0;
        } else {
                /* Start failed. Remove request and indicate failure. */
@@ -790,11 +790,11 @@ __tape_start_next_request(struct tape_device *device)
 }
 
 static void
-tape_delayed_next_request(void *data)
+tape_delayed_next_request(struct work_struct *work)
 {
-       struct tape_device *    device;
+       struct tape_device *device =
+               container_of(work, struct tape_device, tape_dnr.work);
 
-       device = (struct tape_device *) data;
        DBF_LH(6, "tape_delayed_next_request(%p)\n", device);
        spin_lock_irq(get_ccwdev_lock(device->cdev));
        __tape_start_next_request(device);
index dbfb77b..cbab8d2 100644 (file)
@@ -183,7 +183,7 @@ css_get_ssd_info(struct subchannel *sch)
        page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
        if (!page)
                return -ENOMEM;
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        ret = chsc_get_sch_desc_irq(sch, page);
        if (ret) {
                static int cio_chsc_err_msg;
@@ -197,7 +197,7 @@ css_get_ssd_info(struct subchannel *sch)
                        cio_chsc_err_msg = 1;
                }
        }
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        free_page((unsigned long)page);
        if (!ret) {
                int j, chpid, mask;
@@ -233,7 +233,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        if (j >= 8)
                return 0;
 
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
 
        stsch(sch->schid, &schib);
        if (!schib.pmcw.dnv)
@@ -265,10 +265,10 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        else if (sch->lpm == mask)
                goto out_unreg;
 out_unlock:
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        return 0;
 out_unreg:
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        sch->lpm = 0;
        if (css_enqueue_subchannel_slow(sch->schid)) {
                css_clear_subchannel_slow_list();
@@ -378,12 +378,12 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
                /* Check if a subchannel is newly available. */
                return s390_process_res_acc_new_sch(schid);
 
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
 
        chp_mask = s390_process_res_acc_sch(res_data, sch);
 
        if (chp_mask == 0) {
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
                put_device(&sch->dev);
                return 0;
        }
@@ -397,7 +397,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
        else if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
 
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        put_device(&sch->dev);
        return 0;
 }
@@ -635,21 +635,21 @@ __chp_add(struct subchannel_id schid, void *data)
        if (!sch)
                /* Check if the subchannel is now available. */
                return __chp_add_new_sch(schid);
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        for (i=0; i<8; i++) {
                mask = 0x80 >> i;
                if ((sch->schib.pmcw.pim & mask) &&
                    (sch->schib.pmcw.chpid[i] == chp->id)) {
                        if (stsch(sch->schid, &sch->schib) != 0) {
                                /* Endgame. */
-                               spin_unlock_irq(&sch->lock);
+                               spin_unlock_irq(sch->lock);
                                return -ENXIO;
                        }
                        break;
                }
        }
        if (i==8) {
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
                return 0;
        }
        sch->lpm = ((sch->schib.pmcw.pim &
@@ -660,7 +660,7 @@ __chp_add(struct subchannel_id schid, void *data)
        if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
 
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        put_device(&sch->dev);
        return 0;
 }
@@ -750,7 +750,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
        if (!sch->ssd_info.valid)
                return;
        
-       spin_lock_irqsave(&sch->lock, flags);
+       spin_lock_irqsave(sch->lock, flags);
        old_lpm = sch->lpm;
        for (chp = 0; chp < 8; chp++) {
                if (sch->ssd_info.chpid[chp] != chpid)
@@ -785,7 +785,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
                        sch->driver->verify(&sch->dev);
                break;
        }
-       spin_unlock_irqrestore(&sch->lock, flags);
+       spin_unlock_irqrestore(sch->lock, flags);
 }
 
 static int
index 20aee27..7835a71 100644 (file)
@@ -143,11 +143,11 @@ cio_tpi(void)
                return 1;
        local_bh_disable();
        irq_enter ();
-       spin_lock(&sch->lock);
+       spin_lock(sch->lock);
        memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
        if (sch->driver && sch->driver->irq)
                sch->driver->irq(&sch->dev);
-       spin_unlock(&sch->lock);
+       spin_unlock(sch->lock);
        irq_exit ();
        _local_bh_enable();
        return 1;
@@ -415,6 +415,8 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
        CIO_TRACE_EVENT (2, "ensch");
        CIO_TRACE_EVENT (2, sch->dev.bus_id);
 
+       if (sch_is_pseudo_sch(sch))
+               return -EINVAL;
        ccode = stsch (sch->schid, &sch->schib);
        if (ccode)
                return -ENODEV;
@@ -462,6 +464,8 @@ cio_disable_subchannel (struct subchannel *sch)
        CIO_TRACE_EVENT (2, "dissch");
        CIO_TRACE_EVENT (2, sch->dev.bus_id);
 
+       if (sch_is_pseudo_sch(sch))
+               return 0;
        ccode = stsch (sch->schid, &sch->schib);
        if (ccode == 3)         /* Not operational. */
                return -ENODEV;
@@ -496,6 +500,15 @@ cio_disable_subchannel (struct subchannel *sch)
        return ret;
 }
 
+int cio_create_sch_lock(struct subchannel *sch)
+{
+       sch->lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
+       if (!sch->lock)
+               return -ENOMEM;
+       spin_lock_init(sch->lock);
+       return 0;
+}
+
 /*
  * cio_validate_subchannel()
  *
@@ -513,6 +526,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
 {
        char dbf_txt[15];
        int ccode;
+       int err;
 
        sprintf (dbf_txt, "valsch%x", schid.sch_no);
        CIO_TRACE_EVENT (4, dbf_txt);
@@ -520,9 +534,15 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
        /* Nuke all fields. */
        memset(sch, 0, sizeof(struct subchannel));
 
-       spin_lock_init(&sch->lock);
+       sch->schid = schid;
+       if (cio_is_console(schid)) {
+               sch->lock = cio_get_console_lock();
+       } else {
+               err = cio_create_sch_lock(sch);
+               if (err)
+                       goto out;
+       }
        mutex_init(&sch->reg_mutex);
-
        /* Set a name for the subchannel */
        snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
                  schid.sch_no);
@@ -534,10 +554,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
         *  is not valid.
         */
        ccode = stsch_err (schid, &sch->schib);
-       if (ccode)
-               return (ccode == 3) ? -ENXIO : ccode;
-
-       sch->schid = schid;
+       if (ccode) {
+               err = (ccode == 3) ? -ENXIO : ccode;
+               goto out;
+       }
        /* Copy subchannel type from path management control word. */
        sch->st = sch->schib.pmcw.st;
 
@@ -550,14 +570,16 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
                          "non-I/O subchannel type %04X\n",
                          sch->schid.ssid, sch->schid.sch_no, sch->st);
                /* We stop here for non-io subchannels. */
-               return sch->st;
+               err = sch->st;
+               goto out;
        }
 
        /* Initialization for io subchannels. */
-       if (!sch->schib.pmcw.dnv)
+       if (!sch->schib.pmcw.dnv) {
                /* io subchannel but device number is invalid. */
-               return -ENODEV;
-
+               err = -ENODEV;
+               goto out;
+       }
        /* Devno is valid. */
        if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
                /*
@@ -567,7 +589,8 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
                CIO_MSG_EVENT(0, "Blacklisted device detected "
                              "at devno %04X, subchannel set %x\n",
                              sch->schib.pmcw.dev, sch->schid.ssid);
-               return -ENODEV;
+               err = -ENODEV;
+               goto out;
        }
        sch->opm = 0xff;
        if (!cio_is_console(sch->schid))
@@ -595,6 +618,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
        if ((sch->lpm & (sch->lpm - 1)) != 0)
                sch->schib.pmcw.mp = 1; /* multipath mode */
        return 0;
+out:
+       if (!cio_is_console(schid))
+               kfree(sch->lock);
+       sch->lock = NULL;
+       return err;
 }
 
 /*
@@ -637,7 +665,7 @@ do_IRQ (struct pt_regs *regs)
                }
                sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
                if (sch)
-                       spin_lock(&sch->lock);
+                       spin_lock(sch->lock);
                /* Store interrupt response block to lowcore. */
                if (tsch (tpi_info->schid, irb) == 0 && sch) {
                        /* Keep subchannel information word up to date. */
@@ -648,7 +676,7 @@ do_IRQ (struct pt_regs *regs)
                                sch->driver->irq(&sch->dev);
                }
                if (sch)
-                       spin_unlock(&sch->lock);
+                       spin_unlock(sch->lock);
                /*
                 * Are more interrupts pending?
                 * If so, the tpi instruction will update the lowcore
@@ -687,10 +715,10 @@ wait_cons_dev (void)
        __ctl_load (cr6, 6, 6);
 
        do {
-               spin_unlock(&console_subchannel.lock);
+               spin_unlock(console_subchannel.lock);
                if (!cio_tpi())
                        cpu_relax();
-               spin_lock(&console_subchannel.lock);
+               spin_lock(console_subchannel.lock);
        } while (console_subchannel.schib.scsw.actl != 0);
        /*
         * restore previous isc value
index 4541c1a..35154a2 100644 (file)
@@ -87,7 +87,7 @@ struct orb {
 /* subchannel data structure used by I/O subroutines */
 struct subchannel {
        struct subchannel_id schid;
-       spinlock_t lock;        /* subchannel lock */
+       spinlock_t *lock;       /* subchannel lock */
        struct mutex reg_mutex;
        enum {
                SUBCHANNEL_TYPE_IO = 0,
@@ -131,15 +131,19 @@ extern int cio_set_options (struct subchannel *, int);
 extern int cio_get_options (struct subchannel *);
 extern int cio_modify (struct subchannel *);
 
+int cio_create_sch_lock(struct subchannel *);
+
 /* Use with care. */
 #ifdef CONFIG_CCW_CONSOLE
 extern struct subchannel *cio_probe_console(void);
 extern void cio_release_console(void);
 extern int cio_is_console(struct subchannel_id);
 extern struct subchannel *cio_get_console_subchannel(void);
+extern spinlock_t * cio_get_console_lock(void);
 #else
 #define cio_is_console(schid) 0
 #define cio_get_console_subchannel() NULL
+#define cio_get_console_lock() NULL;
 #endif
 
 extern int cio_show_msg;
index 26cf2f5..4c81d89 100644 (file)
@@ -91,9 +91,9 @@ css_free_subchannel(struct subchannel *sch)
                /* Reset intparm to zeroes. */
                sch->schib.pmcw.intparm = 0;
                cio_modify(sch);
+               kfree(sch->lock);
                kfree(sch);
        }
-       
 }
 
 static void
@@ -102,8 +102,10 @@ css_subchannel_release(struct device *dev)
        struct subchannel *sch;
 
        sch = to_subchannel(dev);
-       if (!cio_is_console(sch->schid))
+       if (!cio_is_console(sch->schid)) {
+               kfree(sch->lock);
                kfree(sch);
+       }
 }
 
 extern int css_get_ssd_info(struct subchannel *sch);
@@ -135,14 +137,16 @@ css_register_subchannel(struct subchannel *sch)
        sch->dev.parent = &css[0]->device;
        sch->dev.bus = &css_bus_type;
        sch->dev.release = &css_subchannel_release;
-       
+       sch->dev.groups = subch_attr_groups;
+
        /* make it known to the system */
        ret = css_sch_device_register(sch);
-       if (ret)
+       if (ret) {
                printk (KERN_WARNING "%s: could not register %s\n",
                        __func__, sch->dev.bus_id);
-       else
-               css_get_ssd_info(sch);
+               return ret;
+       }
+       css_get_ssd_info(sch);
        return ret;
 }
 
@@ -201,18 +205,18 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
        unsigned long flags;
        enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
 
-       spin_lock_irqsave(&sch->lock, flags);
+       spin_lock_irqsave(sch->lock, flags);
        disc = device_is_disconnected(sch);
        if (disc && slow) {
                /* Disconnected devices are evaluated directly only.*/
-               spin_unlock_irqrestore(&sch->lock, flags);
+               spin_unlock_irqrestore(sch->lock, flags);
                return 0;
        }
        /* No interrupt after machine check - kill pending timers. */
        device_kill_pending_timer(sch);
        if (!disc && !slow) {
                /* Non-disconnected devices are evaluated on the slow path. */
-               spin_unlock_irqrestore(&sch->lock, flags);
+               spin_unlock_irqrestore(sch->lock, flags);
                return -EAGAIN;
        }
        event = css_get_subchannel_status(sch);
@@ -237,9 +241,9 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
                /* Ask driver what to do with device. */
                action = UNREGISTER;
                if (sch->driver && sch->driver->notify) {
-                       spin_unlock_irqrestore(&sch->lock, flags);
+                       spin_unlock_irqrestore(sch->lock, flags);
                        ret = sch->driver->notify(&sch->dev, event);
-                       spin_lock_irqsave(&sch->lock, flags);
+                       spin_lock_irqsave(sch->lock, flags);
                        if (ret)
                                action = NONE;
                }
@@ -264,9 +268,9 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
        case UNREGISTER:
        case UNREGISTER_PROBE:
                /* Unregister device (will use subchannel lock). */
-               spin_unlock_irqrestore(&sch->lock, flags);
+               spin_unlock_irqrestore(sch->lock, flags);
                css_sch_device_unregister(sch);
-               spin_lock_irqsave(&sch->lock, flags);
+               spin_lock_irqsave(sch->lock, flags);
 
                /* Reset intparm to zeroes. */
                sch->schib.pmcw.intparm = 0;
@@ -278,7 +282,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
        default:
                break;
        }
-       spin_unlock_irqrestore(&sch->lock, flags);
+       spin_unlock_irqrestore(sch->lock, flags);
        /* Probe if necessary. */
        if (action == UNREGISTER_PROBE)
                ret = css_probe_device(sch->schid);
@@ -573,12 +577,24 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr,
 
 static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store);
 
-static inline void __init
-setup_css(int nr)
+static inline int __init setup_css(int nr)
 {
        u32 tod_high;
+       int ret;
 
        memset(css[nr], 0, sizeof(struct channel_subsystem));
+       css[nr]->pseudo_subchannel =
+               kzalloc(sizeof(*css[nr]->pseudo_subchannel), GFP_KERNEL);
+       if (!css[nr]->pseudo_subchannel)
+               return -ENOMEM;
+       css[nr]->pseudo_subchannel->dev.parent = &css[nr]->device;
+       css[nr]->pseudo_subchannel->dev.release = css_subchannel_release;
+       sprintf(css[nr]->pseudo_subchannel->dev.bus_id, "defunct");
+       ret = cio_create_sch_lock(css[nr]->pseudo_subchannel);
+       if (ret) {
+               kfree(css[nr]->pseudo_subchannel);
+               return ret;
+       }
        mutex_init(&css[nr]->mutex);
        css[nr]->valid = 1;
        css[nr]->cssid = nr;
@@ -586,6 +602,7 @@ setup_css(int nr)
        css[nr]->device.release = channel_subsystem_release;
        tod_high = (u32) (get_clock() >> 32);
        css_generate_pgid(css[nr], tod_high);
+       return 0;
 }
 
 /*
@@ -622,10 +639,12 @@ init_channel_subsystem (void)
                        ret = -ENOMEM;
                        goto out_unregister;
                }
-               setup_css(i);
-               ret = device_register(&css[i]->device);
+               ret = setup_css(i);
                if (ret)
                        goto out_free;
+               ret = device_register(&css[i]->device);
+               if (ret)
+                       goto out_free_all;
                if (css_characteristics_avail &&
                    css_chsc_characteristics.secm) {
                        ret = device_create_file(&css[i]->device,
@@ -633,6 +652,9 @@ init_channel_subsystem (void)
                        if (ret)
                                goto out_device;
                }
+               ret = device_register(&css[i]->pseudo_subchannel->dev);
+               if (ret)
+                       goto out_file;
        }
        css_init_done = 1;
 
@@ -640,13 +662,19 @@ init_channel_subsystem (void)
 
        for_each_subchannel(__init_channel_subsystem, NULL);
        return 0;
+out_file:
+       device_remove_file(&css[i]->device, &dev_attr_cm_enable);
 out_device:
        device_unregister(&css[i]->device);
+out_free_all:
+       kfree(css[i]->pseudo_subchannel->lock);
+       kfree(css[i]->pseudo_subchannel);
 out_free:
        kfree(css[i]);
 out_unregister:
        while (i > 0) {
                i--;
+               device_unregister(&css[i]->pseudo_subchannel->dev);
                if (css_characteristics_avail && css_chsc_characteristics.secm)
                        device_remove_file(&css[i]->device,
                                           &dev_attr_cm_enable);
@@ -658,6 +686,11 @@ out:
        return ret;
 }
 
+int sch_is_pseudo_sch(struct subchannel *sch)
+{
+       return sch == to_css(sch->dev.parent)->pseudo_subchannel;
+}
+
 /*
  * find a driver for a subchannel. They identify by the subchannel
  * type with the exception that the console subchannel driver has its own
index 9ff064e..3464c5b 100644 (file)
@@ -73,6 +73,8 @@ struct senseid {
 }  __attribute__ ((packed,aligned(4)));
 
 struct ccw_device_private {
+       struct ccw_device *cdev;
+       struct subchannel *sch;
        int state;              /* device state */
        atomic_t onoff;
        unsigned long registered;
@@ -158,6 +160,8 @@ struct channel_subsystem {
        int cm_enabled;
        void *cub_addr1;
        void *cub_addr2;
+       /* for orphaned ccw devices */
+       struct subchannel *pseudo_subchannel;
 };
 #define to_css(dev) container_of(dev, struct channel_subsystem, device)
 
@@ -185,6 +189,11 @@ void css_clear_subchannel_slow_list(void);
 int css_slow_subchannels_exist(void);
 extern int need_rescan;
 
+int sch_is_pseudo_sch(struct subchannel *);
+
 extern struct workqueue_struct *slow_path_wq;
 extern struct work_struct slow_path_work;
+
+int subchannel_add_files (struct device *);
+extern struct attribute_group *subch_attr_groups[];
 #endif
index d3d3716..8035790 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/param.h>         /* HZ */
 
 #include "cio.h"
+#include "cio_debug.h"
 #include "css.h"
 #include "device.h"
 #include "ioasm.h"
@@ -234,9 +235,11 @@ chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
        ssize_t ret = 0;
        int chp;
 
-       for (chp = 0; chp < 8; chp++)
-               ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
-
+       if (ssd)
+               for (chp = 0; chp < 8; chp++)
+                       ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]);
+       else
+               ret += sprintf (buf, "n/a");
        ret += sprintf (buf+ret, "\n");
        return min((ssize_t)PAGE_SIZE, ret);
 }
@@ -294,14 +297,44 @@ online_show (struct device *dev, struct device_attribute *attr, char *buf)
        return sprintf(buf, cdev->online ? "1\n" : "0\n");
 }
 
+int ccw_device_is_orphan(struct ccw_device *cdev)
+{
+       return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent));
+}
+
+static void ccw_device_unregister(struct work_struct *work)
+{
+       struct ccw_device_private *priv;
+       struct ccw_device *cdev;
+
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
+       if (test_and_clear_bit(1, &cdev->private->registered))
+               device_unregister(&cdev->dev);
+       put_device(&cdev->dev);
+}
+
 static void
 ccw_device_remove_disconnected(struct ccw_device *cdev)
 {
        struct subchannel *sch;
+       unsigned long flags;
        /*
         * Forced offline in disconnected state means
         * 'throw away device'.
         */
+       if (ccw_device_is_orphan(cdev)) {
+               /* Deregister ccw device. */
+               spin_lock_irqsave(cdev->ccwlock, flags);
+               cdev->private->state = DEV_STATE_NOT_OPER;
+               spin_unlock_irqrestore(cdev->ccwlock, flags);
+               if (get_device(&cdev->dev)) {
+                       PREPARE_WORK(&cdev->private->kick_work,
+                                    ccw_device_unregister);
+                       queue_work(ccw_device_work, &cdev->private->kick_work);
+               }
+               return ;
+       }
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
@@ -462,6 +495,8 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf)
        struct ccw_device *cdev = to_ccwdev(dev);
        struct subchannel *sch;
 
+       if (ccw_device_is_orphan(cdev))
+               return sprintf(buf, "no device\n");
        switch (cdev->private->state) {
        case DEV_STATE_BOXED:
                return sprintf(buf, "boxed\n");
@@ -498,11 +533,10 @@ static struct attribute_group subch_attr_group = {
        .attrs = subch_attrs,
 };
 
-static inline int
-subchannel_add_files (struct device *dev)
-{
-       return sysfs_create_group(&dev->kobj, &subch_attr_group);
-}
+struct attribute_group *subch_attr_groups[] = {
+       &subch_attr_group,
+       NULL,
+};
 
 static struct attribute * ccwdev_attrs[] = {
        &dev_attr_devtype.attr,
@@ -563,11 +597,10 @@ match_devno(struct device * dev, void * data)
 
        cdev = to_ccwdev(dev);
        if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
+           !ccw_device_is_orphan(cdev) &&
            ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
-           (cdev != d->sibling)) {
-               cdev->private->state = DEV_STATE_NOT_OPER;
+           (cdev != d->sibling))
                return 1;
-       }
        return 0;
 }
 
@@ -584,13 +617,36 @@ static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,
        return dev ? to_ccwdev(dev) : NULL;
 }
 
-static void
-ccw_device_add_changed(void *data)
+static int match_orphan(struct device *dev, void *data)
+{
+       struct ccw_dev_id *dev_id;
+       struct ccw_device *cdev;
+
+       dev_id = data;
+       cdev = to_ccwdev(dev);
+       return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
+}
+
+static struct ccw_device *
+get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,
+                             struct ccw_dev_id *dev_id)
 {
+       struct device *dev;
 
+       dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,
+                               match_orphan);
+
+       return dev ? to_ccwdev(dev) : NULL;
+}
+
+static void
+ccw_device_add_changed(struct work_struct *work)
+{
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        if (device_add(&cdev->dev)) {
                put_device(&cdev->dev);
                return;
@@ -602,64 +658,21 @@ ccw_device_add_changed(void *data)
        }
 }
 
-extern int css_get_ssd_info(struct subchannel *sch);
-
-void
-ccw_device_do_unreg_rereg(void *data)
+void ccw_device_do_unreg_rereg(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
-       int need_rename;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
-       if (cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
-               /*
-                * The device number has changed. This is usually only when
-                * a device has been detached under VM and then re-appeared
-                * on another subchannel because of a different attachment
-                * order than before. Ideally, we should should just switch
-                * subchannels, but unfortunately, this is not possible with
-                * the current implementation.
-                * Instead, we search for the old subchannel for this device
-                * number and deregister so there are no collisions with the
-                * newly registered ccw_device.
-                * FIXME: Find another solution so the block layer doesn't
-                *        get possibly sick...
-                */
-               struct ccw_device *other_cdev;
-               struct ccw_dev_id dev_id;
-
-               need_rename = 1;
-               dev_id.devno = sch->schib.pmcw.dev;
-               dev_id.ssid = sch->schid.ssid;
-               other_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
-               if (other_cdev) {
-                       struct subchannel *other_sch;
-
-                       other_sch = to_subchannel(other_cdev->dev.parent);
-                       if (get_device(&other_sch->dev)) {
-                               stsch(other_sch->schid, &other_sch->schib);
-                               if (other_sch->schib.pmcw.dnv) {
-                                       other_sch->schib.pmcw.intparm = 0;
-                                       cio_modify(other_sch);
-                               }
-                               css_sch_device_unregister(other_sch);
-                       }
-               }
-               /* Update ssd info here. */
-               css_get_ssd_info(sch);
-               cdev->private->dev_id.devno = sch->schib.pmcw.dev;
-       } else
-               need_rename = 0;
+
        device_remove_files(&cdev->dev);
        if (test_and_clear_bit(1, &cdev->private->registered))
                device_del(&cdev->dev);
-       if (need_rename)
-               snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
-                         sch->schid.ssid, sch->schib.pmcw.dev);
        PREPARE_WORK(&cdev->private->kick_work,
-                    ccw_device_add_changed, cdev);
+                    ccw_device_add_changed);
        queue_work(ccw_device_work, &cdev->private->kick_work);
 }
 
@@ -673,18 +686,194 @@ ccw_device_release(struct device *dev)
        kfree(cdev);
 }
 
+static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       cdev  = kzalloc(sizeof(*cdev), GFP_KERNEL);
+       if (cdev) {
+               cdev->private = kzalloc(sizeof(struct ccw_device_private),
+                                       GFP_KERNEL | GFP_DMA);
+               if (cdev->private)
+                       return cdev;
+       }
+       kfree(cdev);
+       return ERR_PTR(-ENOMEM);
+}
+
+static int io_subchannel_initialize_dev(struct subchannel *sch,
+                                       struct ccw_device *cdev)
+{
+       cdev->private->cdev = cdev;
+       atomic_set(&cdev->private->onoff, 0);
+       cdev->dev.parent = &sch->dev;
+       cdev->dev.release = ccw_device_release;
+       INIT_LIST_HEAD(&cdev->private->kick_work.entry);
+       /* Do first half of device_register. */
+       device_initialize(&cdev->dev);
+       if (!get_device(&sch->dev)) {
+               if (cdev->dev.release)
+                       cdev->dev.release(&cdev->dev);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+       int ret;
+
+       cdev = io_subchannel_allocate_dev(sch);
+       if (!IS_ERR(cdev)) {
+               ret = io_subchannel_initialize_dev(sch, cdev);
+               if (ret) {
+                       kfree(cdev);
+                       cdev = ERR_PTR(ret);
+               }
+       }
+       return cdev;
+}
+
+static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
+
+static void sch_attach_device(struct subchannel *sch,
+                             struct ccw_device *cdev)
+{
+       spin_lock_irq(sch->lock);
+       sch->dev.driver_data = cdev;
+       cdev->private->schid = sch->schid;
+       cdev->ccwlock = sch->lock;
+       device_trigger_reprobe(sch);
+       spin_unlock_irq(sch->lock);
+}
+
+static void sch_attach_disconnected_device(struct subchannel *sch,
+                                          struct ccw_device *cdev)
+{
+       struct subchannel *other_sch;
+       int ret;
+
+       other_sch = to_subchannel(get_device(cdev->dev.parent));
+       ret = device_move(&cdev->dev, &sch->dev);
+       if (ret) {
+               CIO_MSG_EVENT(2, "Moving disconnected device 0.%x.%04x failed "
+                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno, ret);
+               put_device(&other_sch->dev);
+               return;
+       }
+       other_sch->dev.driver_data = NULL;
+       /* No need to keep a subchannel without ccw device around. */
+       css_sch_device_unregister(other_sch);
+       put_device(&other_sch->dev);
+       sch_attach_device(sch, cdev);
+}
+
+static void sch_attach_orphaned_device(struct subchannel *sch,
+                                      struct ccw_device *cdev)
+{
+       int ret;
+
+       /* Try to move the ccw device to its new subchannel. */
+       ret = device_move(&cdev->dev, &sch->dev);
+       if (ret) {
+               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
+                             "failed (ret=%d)!\n",
+                             cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno, ret);
+               return;
+       }
+       sch_attach_device(sch, cdev);
+}
+
+static void sch_create_and_recog_new_device(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       /* Need to allocate a new ccw device. */
+       cdev = io_subchannel_create_ccwdev(sch);
+       if (IS_ERR(cdev)) {
+               /* OK, we did everything we could... */
+               css_sch_device_unregister(sch);
+               return;
+       }
+       spin_lock_irq(sch->lock);
+       sch->dev.driver_data = cdev;
+       spin_unlock_irq(sch->lock);
+       /* Start recognition for the new ccw device. */
+       if (io_subchannel_recog(cdev, sch)) {
+               spin_lock_irq(sch->lock);
+               sch->dev.driver_data = NULL;
+               spin_unlock_irq(sch->lock);
+               if (cdev->dev.release)
+                       cdev->dev.release(&cdev->dev);
+               css_sch_device_unregister(sch);
+       }
+}
+
+
+void ccw_device_move_to_orphanage(struct work_struct *work)
+{
+       struct ccw_device_private *priv;
+       struct ccw_device *cdev;
+       struct ccw_device *replacing_cdev;
+       struct subchannel *sch;
+       int ret;
+       struct channel_subsystem *css;
+       struct ccw_dev_id dev_id;
+
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
+       sch = to_subchannel(cdev->dev.parent);
+       css = to_css(sch->dev.parent);
+       dev_id.devno = sch->schib.pmcw.dev;
+       dev_id.ssid = sch->schid.ssid;
+
+       /*
+        * Move the orphaned ccw device to the orphanage so the replacing
+        * ccw device can take its place on the subchannel.
+        */
+       ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
+       if (ret) {
+               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
+                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno, ret);
+               return;
+       }
+       cdev->ccwlock = css->pseudo_subchannel->lock;
+       /*
+        * Search for the replacing ccw device
+        * - among the disconnected devices
+        * - in the orphanage
+        */
+       replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
+       if (replacing_cdev) {
+               sch_attach_disconnected_device(sch, replacing_cdev);
+               return;
+       }
+       replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
+       if (replacing_cdev) {
+               sch_attach_orphaned_device(sch, replacing_cdev);
+               return;
+       }
+       sch_create_and_recog_new_device(sch);
+}
+
 /*
  * Register recognized device.
  */
 static void
-io_subchannel_register(void *data)
+io_subchannel_register(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
        unsigned long flags;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
 
        /*
@@ -709,9 +898,9 @@ io_subchannel_register(void *data)
                printk (KERN_WARNING "%s: could not register %s\n",
                        __func__, cdev->dev.bus_id);
                put_device(&cdev->dev);
-               spin_lock_irqsave(&sch->lock, flags);
+               spin_lock_irqsave(sch->lock, flags);
                sch->dev.driver_data = NULL;
-               spin_unlock_irqrestore(&sch->lock, flags);
+               spin_unlock_irqrestore(sch->lock, flags);
                kfree (cdev->private);
                kfree (cdev);
                put_device(&sch->dev);
@@ -719,11 +908,6 @@ io_subchannel_register(void *data)
                        wake_up(&ccw_device_init_wq);
                return;
        }
-
-       ret = subchannel_add_files(cdev->dev.parent);
-       if (ret)
-               printk(KERN_WARNING "%s: could not add attributes to %s\n",
-                      __func__, sch->dev.bus_id);
        put_device(&cdev->dev);
 out:
        cdev->private->flags.recog_done = 1;
@@ -734,11 +918,14 @@ out:
 }
 
 void
-ccw_device_call_sch_unregister(void *data)
+ccw_device_call_sch_unregister(struct work_struct *work)
 {
-       struct ccw_device *cdev = data;
+       struct ccw_device_private *priv;
+       struct ccw_device *cdev;
        struct subchannel *sch;
 
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
@@ -768,7 +955,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                        break;
                sch = to_subchannel(cdev->dev.parent);
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, cdev);
+                            ccw_device_call_sch_unregister);
                queue_work(slow_path_wq, &cdev->private->kick_work);
                if (atomic_dec_and_test(&ccw_device_init_count))
                        wake_up(&ccw_device_init_wq);
@@ -783,7 +970,7 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                if (!get_device(&cdev->dev))
                        break;
                PREPARE_WORK(&cdev->private->kick_work,
-                            io_subchannel_register, cdev);
+                            io_subchannel_register);
                queue_work(slow_path_wq, &cdev->private->kick_work);
                break;
        }
@@ -797,7 +984,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 
        sch->dev.driver_data = cdev;
        sch->driver = &io_subchannel_driver;
-       cdev->ccwlock = &sch->lock;
+       cdev->ccwlock = sch->lock;
 
        /* Init private data. */
        priv = cdev->private;
@@ -817,9 +1004,9 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
        atomic_inc(&ccw_device_init_count);
 
        /* Start async. device sensing. */
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        rc = ccw_device_recognition(cdev);
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        if (rc) {
                if (atomic_dec_and_test(&ccw_device_init_count))
                        wake_up(&ccw_device_init_wq);
@@ -827,12 +1014,55 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
        return rc;
 }
 
+static void ccw_device_move_to_sch(struct work_struct *work)
+{
+       struct ccw_device_private *priv;
+       int rc;
+       struct subchannel *sch;
+       struct ccw_device *cdev;
+       struct subchannel *former_parent;
+
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       sch = priv->sch;
+       cdev = priv->cdev;
+       former_parent = ccw_device_is_orphan(cdev) ?
+               NULL : to_subchannel(get_device(cdev->dev.parent));
+       mutex_lock(&sch->reg_mutex);
+       /* Try to move the ccw device to its new subchannel. */
+       rc = device_move(&cdev->dev, &sch->dev);
+       mutex_unlock(&sch->reg_mutex);
+       if (rc) {
+               CIO_MSG_EVENT(2, "Moving device 0.%x.%04x to subchannel "
+                             "0.%x.%04x failed (ret=%d)!\n",
+                             cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno, sch->schid.ssid,
+                             sch->schid.sch_no, rc);
+               css_sch_device_unregister(sch);
+               goto out;
+       }
+       if (former_parent) {
+               spin_lock_irq(former_parent->lock);
+               former_parent->dev.driver_data = NULL;
+               spin_unlock_irq(former_parent->lock);
+               css_sch_device_unregister(former_parent);
+               /* Reset intparm to zeroes. */
+               former_parent->schib.pmcw.intparm = 0;
+               cio_modify(former_parent);
+       }
+       sch_attach_device(sch, cdev);
+out:
+       if (former_parent)
+               put_device(&former_parent->dev);
+       put_device(&cdev->dev);
+}
+
 static int
 io_subchannel_probe (struct subchannel *sch)
 {
        struct ccw_device *cdev;
        int rc;
        unsigned long flags;
+       struct ccw_dev_id dev_id;
 
        if (sch->dev.driver_data) {
                /*
@@ -843,7 +1073,6 @@ io_subchannel_probe (struct subchannel *sch)
                cdev = sch->dev.driver_data;
                device_initialize(&cdev->dev);
                ccw_device_register(cdev);
-               subchannel_add_files(&sch->dev);
                /*
                 * Check if the device is already online. If it is
                 * the reference count needs to be corrected
@@ -856,33 +1085,37 @@ io_subchannel_probe (struct subchannel *sch)
                        get_device(&cdev->dev);
                return 0;
        }
-       cdev = kzalloc (sizeof(*cdev), GFP_KERNEL);
+       /*
+        * First check if a fitting device may be found amongst the
+        * disconnected devices or in the orphanage.
+        */
+       dev_id.devno = sch->schib.pmcw.dev;
+       dev_id.ssid = sch->schid.ssid;
+       cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
        if (!cdev)
-               return -ENOMEM;
-       cdev->private = kzalloc(sizeof(struct ccw_device_private),
-                               GFP_KERNEL | GFP_DMA);
-       if (!cdev->private) {
-               kfree(cdev);
-               return -ENOMEM;
-       }
-       atomic_set(&cdev->private->onoff, 0);
-       cdev->dev.parent = &sch->dev;
-       cdev->dev.release = ccw_device_release;
-       INIT_LIST_HEAD(&cdev->private->kick_work.entry);
-       /* Do first half of device_register. */
-       device_initialize(&cdev->dev);
-
-       if (!get_device(&sch->dev)) {
-               if (cdev->dev.release)
-                       cdev->dev.release(&cdev->dev);
-               return -ENODEV;
+               cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
+                                                    &dev_id);
+       if (cdev) {
+               /*
+                * Schedule moving the device until when we have a registered
+                * subchannel to move to and succeed the probe. We can
+                * unregister later again, when the probe is through.
+                */
+               cdev->private->sch = sch;
+               PREPARE_WORK(&cdev->private->kick_work,
+                            ccw_device_move_to_sch);
+               queue_work(slow_path_wq, &cdev->private->kick_work);
+               return 0;
        }
+       cdev = io_subchannel_create_ccwdev(sch);
+       if (IS_ERR(cdev))
+               return PTR_ERR(cdev);
 
        rc = io_subchannel_recog(cdev, sch);
        if (rc) {
-               spin_lock_irqsave(&sch->lock, flags);
+               spin_lock_irqsave(sch->lock, flags);
                sch->dev.driver_data = NULL;
-               spin_unlock_irqrestore(&sch->lock, flags);
+               spin_unlock_irqrestore(sch->lock, flags);
                if (cdev->dev.release)
                        cdev->dev.release(&cdev->dev);
        }
@@ -890,17 +1123,6 @@ io_subchannel_probe (struct subchannel *sch)
        return rc;
 }
 
-static void
-ccw_device_unregister(void *data)
-{
-       struct ccw_device *cdev;
-
-       cdev = (struct ccw_device *)data;
-       if (test_and_clear_bit(1, &cdev->private->registered))
-               device_unregister(&cdev->dev);
-       put_device(&cdev->dev);
-}
-
 static int
 io_subchannel_remove (struct subchannel *sch)
 {
@@ -921,7 +1143,7 @@ io_subchannel_remove (struct subchannel *sch)
         */
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_unregister, cdev);
+                            ccw_device_unregister);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        return 0;
@@ -1003,6 +1225,13 @@ static struct ccw_device console_cdev;
 static struct ccw_device_private console_private;
 static int console_cdev_in_use;
 
+static DEFINE_SPINLOCK(ccw_console_lock);
+
+spinlock_t * cio_get_console_lock(void)
+{
+       return &ccw_console_lock;
+}
+
 static int
 ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
 {
@@ -1048,6 +1277,7 @@ ccw_device_probe_console(void)
        memset(&console_cdev, 0, sizeof(struct ccw_device));
        memset(&console_private, 0, sizeof(struct ccw_device_private));
        console_cdev.private = &console_private;
+       console_private.cdev = &console_cdev;
        ret = ccw_device_console_enable(&console_cdev, sch);
        if (ret) {
                cio_release_console();
index 9233b5c..29db634 100644 (file)
@@ -78,8 +78,10 @@ void io_subchannel_recog_done(struct ccw_device *cdev);
 
 int ccw_device_cancel_halt_clear(struct ccw_device *);
 
-void ccw_device_do_unreg_rereg(void *);
-void ccw_device_call_sch_unregister(void *);
+void ccw_device_do_unreg_rereg(struct work_struct *);
+void ccw_device_call_sch_unregister(struct work_struct *);
+void ccw_device_move_to_orphanage(struct work_struct *);
+int ccw_device_is_orphan(struct ccw_device *);
 
 int ccw_device_recognition(struct ccw_device *);
 int ccw_device_online(struct ccw_device *);
index 09c7672..eed1457 100644 (file)
@@ -186,15 +186,14 @@ ccw_device_handle_oper(struct ccw_device *cdev)
        /*
         * Check if cu type and device type still match. If
         * not, it is certainly another device and we have to
-        * de- and re-register. Also check here for non-matching devno.
+        * de- and re-register.
         */
        if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
            cdev->id.cu_model != cdev->private->senseid.cu_model ||
            cdev->id.dev_type != cdev->private->senseid.dev_type ||
-           cdev->id.dev_model != cdev->private->senseid.dev_model ||
-           cdev->private->dev_id.devno != sch->schib.pmcw.dev) {
+           cdev->id.dev_model != cdev->private->senseid.dev_model) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_do_unreg_rereg, cdev);
+                            ccw_device_do_unreg_rereg);
                queue_work(ccw_device_work, &cdev->private->kick_work);
                return 0;
        }
@@ -329,19 +328,21 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
 }
 
 static void
-ccw_device_oper_notify(void *data)
+ccw_device_oper_notify(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        ret = (sch->driver && sch->driver->notify) ?
                sch->driver->notify(&sch->dev, CIO_OPER) : 0;
        if (!ret)
                /* Driver doesn't want device back. */
-               ccw_device_do_unreg_rereg(cdev);
+               ccw_device_do_unreg_rereg(work);
        else {
                /* Reenable channel measurements, if needed. */
                cmf_reenable(cdev);
@@ -377,8 +378,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
        if (cdev->private->flags.donotify) {
                cdev->private->flags.donotify = 0;
-               PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify,
-                            cdev);
+               PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -528,13 +528,15 @@ ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
 
 
 static void
-ccw_device_nopath_notify(void *data)
+ccw_device_nopath_notify(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
 
-       cdev = data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        /* Extra sanity. */
        if (sch->lpm)
@@ -547,8 +549,7 @@ ccw_device_nopath_notify(void *data)
                        cio_disable_subchannel(sch);
                        if (get_device(&cdev->dev)) {
                                PREPARE_WORK(&cdev->private->kick_work,
-                                            ccw_device_call_sch_unregister,
-                                            cdev);
+                                            ccw_device_call_sch_unregister);
                                queue_work(ccw_device_work,
                                           &cdev->private->kick_work);
                        } else
@@ -607,7 +608,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
@@ -674,6 +675,10 @@ ccw_device_offline(struct ccw_device *cdev)
 {
        struct subchannel *sch;
 
+       if (ccw_device_is_orphan(cdev)) {
+               ccw_device_done(cdev, DEV_STATE_OFFLINE);
+               return 0;
+       }
        sch = to_subchannel(cdev->dev.parent);
        if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
                return -ENODEV;
@@ -738,7 +743,7 @@ ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event)
        sch = to_subchannel(cdev->dev.parent);
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, cdev);
+                            ccw_device_call_sch_unregister);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -769,7 +774,7 @@ ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event)
        }
        if (get_device(&cdev->dev)) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_call_sch_unregister, cdev);
+                            ccw_device_call_sch_unregister);
                queue_work(ccw_device_work, &cdev->private->kick_work);
        }
        wake_up(&cdev->private->wait_q);
@@ -874,7 +879,7 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event)
                sch = to_subchannel(cdev->dev.parent);
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -969,7 +974,7 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
                              ERR_PTR(-EIO));
        if (!sch->lpm) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        } else if (cdev->private->flags.doverify)
                /* Start delayed path verification. */
@@ -992,7 +997,7 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
                sch = to_subchannel(cdev->dev.parent);
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -1021,7 +1026,7 @@ void device_kill_io(struct subchannel *sch)
        if (ret == -ENODEV) {
                if (!sch->lpm) {
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_nopath_notify, cdev);
+                                    ccw_device_nopath_notify);
                        queue_work(ccw_device_notify_work,
                                   &cdev->private->kick_work);
                } else
@@ -1033,7 +1038,7 @@ void device_kill_io(struct subchannel *sch)
                              ERR_PTR(-EIO));
        if (!sch->lpm) {
                PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_nopath_notify, cdev);
+                            ccw_device_nopath_notify);
                queue_work(ccw_device_notify_work, &cdev->private->kick_work);
        } else
                /* Start delayed path verification. */
@@ -1104,7 +1109,8 @@ device_trigger_reprobe(struct subchannel *sch)
        /* Update some values. */
        if (stsch(sch->schid, &sch->schib))
                return;
-
+       if (!sch->schib.pmcw.dnv)
+               return;
        /*
         * The pim, pam, pom values may not be accurate, but they are the best
         * we have before performing device selection :/
@@ -1118,7 +1124,13 @@ device_trigger_reprobe(struct subchannel *sch)
                sch->schib.pmcw.mp = 1;
        sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
        /* We should also udate ssd info, but this has to wait. */
-       ccw_device_start_id(cdev, 0);
+       /* Check if this is another device which appeared on the same sch. */
+       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
+               PREPARE_WORK(&cdev->private->kick_work,
+                            ccw_device_move_to_orphanage);
+               queue_work(ccw_device_work, &cdev->private->kick_work);
+       } else
+               ccw_device_start_id(cdev, 0);
 }
 
 static void
index b39c1fa..d269607 100644 (file)
@@ -316,9 +316,9 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
                        ccw_device_set_timeout(cdev, 0);
                if (ret == -EBUSY) {
                        /* Try again later. */
-                       spin_unlock_irq(&sch->lock);
+                       spin_unlock_irq(sch->lock);
                        msleep(10);
-                       spin_lock_irq(&sch->lock);
+                       spin_lock_irq(sch->lock);
                        continue;
                }
                if (ret != 0)
@@ -326,12 +326,12 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
                        break;
                /* Wait for end of request. */
                cdev->private->intparm = magic;
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
                wait_event(cdev->private->wait_q,
                           (cdev->private->intparm == -EIO) ||
                           (cdev->private->intparm == -EAGAIN) ||
                           (cdev->private->intparm == 0));
-               spin_lock_irq(&sch->lock);
+               spin_lock_irq(sch->lock);
                /* Check at least for channel end / device end */
                if (cdev->private->intparm == -EIO) {
                        /* Non-retryable error. */
@@ -342,9 +342,9 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
                        /* Success. */
                        break;
                /* Try again later. */
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
                msleep(10);
-               spin_lock_irq(&sch->lock);
+               spin_lock_irq(sch->lock);
        } while (1);
 
        return ret;
@@ -389,7 +389,7 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
                return ret;
        }
 
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        /* Save interrupt handler. */
        handler = cdev->handler;
        /* Temporarily install own handler. */
@@ -406,7 +406,7 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
 
        /* Restore interrupt handler. */
        cdev->handler = handler;
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
 
        clear_normalized_cda (rdc_ccw);
        kfree(rdc_ccw);
@@ -463,7 +463,7 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
        rcd_ccw->count = ciw->count;
        rcd_ccw->flags = CCW_FLAG_SLI;
 
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        /* Save interrupt handler. */
        handler = cdev->handler;
        /* Temporarily install own handler. */
@@ -480,7 +480,7 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
 
        /* Restore interrupt handler. */
        cdev->handler = handler;
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
 
        /*
         * on success we update the user input parms
@@ -537,7 +537,7 @@ ccw_device_stlck(struct ccw_device *cdev)
                kfree(buf);
                return -ENOMEM;
        }
-       spin_lock_irqsave(&sch->lock, flags);
+       spin_lock_irqsave(sch->lock, flags);
        ret = cio_enable_subchannel(sch, 3);
        if (ret)
                goto out_unlock;
@@ -559,9 +559,9 @@ ccw_device_stlck(struct ccw_device *cdev)
                goto out_unlock;
        }
        cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
-       spin_unlock_irqrestore(&sch->lock, flags);
+       spin_unlock_irqrestore(sch->lock, flags);
        wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
-       spin_lock_irqsave(&sch->lock, flags);
+       spin_lock_irqsave(sch->lock, flags);
        cio_disable_subchannel(sch); //FIXME: return code?
        if ((cdev->private->irb.scsw.dstat !=
             (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
@@ -572,7 +572,7 @@ ccw_device_stlck(struct ccw_device *cdev)
 out_unlock:
        kfree(buf);
        kfree(buf2);
-       spin_unlock_irqrestore(&sch->lock, flags);
+       spin_unlock_irqrestore(sch->lock, flags);
        return ret;
 }
 
index 8d5fa1b..9d4ea44 100644 (file)
@@ -46,6 +46,7 @@
 #include <asm/timex.h>
 
 #include <asm/debug.h>
+#include <asm/s390_rdev.h>
 #include <asm/qdio.h>
 
 #include "cio.h"
@@ -65,12 +66,12 @@ MODULE_LICENSE("GPL");
 /******************** HERE WE GO ***********************************/
 
 static const char version[] = "QDIO base support version 2";
+extern struct bus_type ccw_bus_type;
 
-#ifdef QDIO_PERFORMANCE_STATS
+static int qdio_performance_stats = 0;
 static int proc_perf_file_registration;
 static unsigned long i_p_c, i_p_nc, o_p_c, o_p_nc, ii_p_c, ii_p_nc;
 static struct qdio_perf_stats perf_stats;
-#endif /* QDIO_PERFORMANCE_STATS */
 
 static int hydra_thinints;
 static int is_passthrough = 0;
@@ -275,9 +276,8 @@ qdio_siga_sync(struct qdio_q *q, unsigned int gpr2,
        QDIO_DBF_TEXT4(0,trace,"sigasync");
        QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.siga_syncs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               perf_stats.siga_syncs++;
 
        cc = do_siga_sync(q->schid, gpr2, gpr3);
        if (cc)
@@ -322,9 +322,8 @@ qdio_siga_output(struct qdio_q *q)
        __u32 busy_bit;
        __u64 start_time=0;
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.siga_outs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               perf_stats.siga_outs++;
 
        QDIO_DBF_TEXT4(0,trace,"sigaout");
        QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
@@ -358,9 +357,8 @@ qdio_siga_input(struct qdio_q *q)
        QDIO_DBF_TEXT4(0,trace,"sigain");
        QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.siga_ins++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               perf_stats.siga_ins++;
 
        cc = do_siga_input(q->schid, q->mask);
        
@@ -954,9 +952,8 @@ __qdio_outbound_processing(struct qdio_q *q)
 
        if (unlikely(qdio_reserve_q(q))) {
                qdio_release_q(q);
-#ifdef QDIO_PERFORMANCE_STATS
-               o_p_c++;
-#endif /* QDIO_PERFORMANCE_STATS */
+               if (qdio_performance_stats)
+                       o_p_c++;
                /* as we're sissies, we'll check next time */
                if (likely(!atomic_read(&q->is_in_shutdown))) {
                        qdio_mark_q(q);
@@ -964,10 +961,10 @@ __qdio_outbound_processing(struct qdio_q *q)
                }
                return;
        }
-#ifdef QDIO_PERFORMANCE_STATS
-       o_p_nc++;
-       perf_stats.tl_runs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               o_p_nc++;
+               perf_stats.tl_runs++;
+       }
 
        /* see comment in qdio_kick_outbound_q */
        siga_attempts=atomic_read(&q->busy_siga_counter);
@@ -1142,15 +1139,16 @@ qdio_has_inbound_q_moved(struct qdio_q *q)
 {
        int i;
 
-#ifdef QDIO_PERFORMANCE_STATS
        static int old_pcis=0;
        static int old_thinints=0;
 
-       if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints))
-               perf_stats.start_time_inbound=NOW;
-       else
-               old_pcis=perf_stats.pcis;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               if ((old_pcis==perf_stats.pcis)&&
+                   (old_thinints==perf_stats.thinints))
+                       perf_stats.start_time_inbound=NOW;
+               else
+                       old_pcis=perf_stats.pcis;
+       }
 
        i=qdio_get_inbound_buffer_frontier(q);
        if ( (i!=GET_SAVED_FRONTIER(q)) ||
@@ -1340,10 +1338,10 @@ qdio_kick_inbound_handler(struct qdio_q *q)
        q->siga_error=0;
        q->error_status_flags=0;
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound;
-       perf_stats.inbound_cnt++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound;
+               perf_stats.inbound_cnt++;
+       }
 }
 
 static inline void
@@ -1363,9 +1361,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
         */
        if (unlikely(qdio_reserve_q(q))) {
                qdio_release_q(q);
-#ifdef QDIO_PERFORMANCE_STATS
-               ii_p_c++;
-#endif /* QDIO_PERFORMANCE_STATS */
+               if (qdio_performance_stats)
+                       ii_p_c++;
                /* 
                 * as we might just be about to stop polling, we make
                 * sure that we check again at least once more 
@@ -1373,9 +1370,8 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
                tiqdio_sched_tl();
                return;
        }
-#ifdef QDIO_PERFORMANCE_STATS
-       ii_p_nc++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               ii_p_nc++;
        if (unlikely(atomic_read(&q->is_in_shutdown))) {
                qdio_unmark_q(q);
                goto out;
@@ -1416,11 +1412,11 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
                irq_ptr = (struct qdio_irq*)q->irq_ptr;
                for (i=0;i<irq_ptr->no_output_qs;i++) {
                        oq = irq_ptr->output_qs[i];
-#ifdef QDIO_PERFORMANCE_STATS
-                       perf_stats.tl_runs--;
-#endif /* QDIO_PERFORMANCE_STATS */
-                       if (!qdio_is_outbound_q_done(oq))
+                       if (!qdio_is_outbound_q_done(oq)) {
+                               if (qdio_performance_stats)
+                                       perf_stats.tl_runs--;
                                __qdio_outbound_processing(oq);
+                       }
                }
        }
 
@@ -1457,9 +1453,8 @@ __qdio_inbound_processing(struct qdio_q *q)
 
        if (unlikely(qdio_reserve_q(q))) {
                qdio_release_q(q);
-#ifdef QDIO_PERFORMANCE_STATS
-               i_p_c++;
-#endif /* QDIO_PERFORMANCE_STATS */
+               if (qdio_performance_stats)
+                       i_p_c++;
                /* as we're sissies, we'll check next time */
                if (likely(!atomic_read(&q->is_in_shutdown))) {
                        qdio_mark_q(q);
@@ -1467,10 +1462,10 @@ __qdio_inbound_processing(struct qdio_q *q)
                }
                return;
        }
-#ifdef QDIO_PERFORMANCE_STATS
-       i_p_nc++;
-       perf_stats.tl_runs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               i_p_nc++;
+               perf_stats.tl_runs++;
+       }
 
 again:
        if (qdio_has_inbound_q_moved(q)) {
@@ -1516,9 +1511,8 @@ tiqdio_reset_processing_state(struct qdio_q *q, int q_laps)
 
        if (unlikely(qdio_reserve_q(q))) {
                qdio_release_q(q);
-#ifdef QDIO_PERFORMANCE_STATS
-               ii_p_c++;
-#endif /* QDIO_PERFORMANCE_STATS */
+               if (qdio_performance_stats)
+                       ii_p_c++;
                /* 
                 * as we might just be about to stop polling, we make
                 * sure that we check again at least once more 
@@ -1609,9 +1603,8 @@ tiqdio_tl(unsigned long data)
 {
        QDIO_DBF_TEXT4(0,trace,"iqdio_tl");
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.tl_runs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               perf_stats.tl_runs++;
 
        tiqdio_inbound_checks();
 }
@@ -1918,10 +1911,10 @@ tiqdio_thinint_handler(void)
 {
        QDIO_DBF_TEXT4(0,trace,"thin_int");
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.thinints++;
-       perf_stats.start_time_inbound=NOW;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               perf_stats.thinints++;
+               perf_stats.start_time_inbound=NOW;
+       }
 
        /* SVS only when needed:
         * issue SVS to benefit from iqdio interrupt avoidance
@@ -1976,18 +1969,17 @@ qdio_handle_pci(struct qdio_irq *irq_ptr)
        int i;
        struct qdio_q *q;
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.pcis++;
-       perf_stats.start_time_inbound=NOW;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               perf_stats.pcis++;
+               perf_stats.start_time_inbound=NOW;
+       }
        for (i=0;i<irq_ptr->no_input_qs;i++) {
                q=irq_ptr->input_qs[i];
                if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT)
                        qdio_mark_q(q);
                else {
-#ifdef QDIO_PERFORMANCE_STATS
-                       perf_stats.tl_runs--;
-#endif /* QDIO_PERFORMANCE_STATS */
+                       if (qdio_performance_stats)
+                               perf_stats.tl_runs--;
                        __qdio_inbound_processing(q);
                }
        }
@@ -1995,11 +1987,10 @@ qdio_handle_pci(struct qdio_irq *irq_ptr)
                return;
        for (i=0;i<irq_ptr->no_output_qs;i++) {
                q=irq_ptr->output_qs[i];
-#ifdef QDIO_PERFORMANCE_STATS
-               perf_stats.tl_runs--;
-#endif /* QDIO_PERFORMANCE_STATS */
                if (qdio_is_outbound_q_done(q))
                        continue;
+               if (qdio_performance_stats)
+                       perf_stats.tl_runs--;
                if (!irq_ptr->sync_done_on_outb_pcis)
                        SYNC_MEMORY;
                __qdio_outbound_processing(q);
@@ -2045,11 +2036,13 @@ omit_handler_call:
 }
 
 static void
-qdio_call_shutdown(void *data)
+qdio_call_shutdown(struct work_struct *work)
 {
+       struct ccw_device_private *priv;
        struct ccw_device *cdev;
 
-       cdev = (struct ccw_device *)data;
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
        put_device(&cdev->dev);
 }
@@ -2091,7 +2084,7 @@ qdio_timeout_handler(struct ccw_device *cdev)
                if (get_device(&cdev->dev)) {
                        /* Can't call shutdown from interrupt context. */
                        PREPARE_WORK(&cdev->private->kick_work,
-                                    qdio_call_shutdown, (void *)cdev);
+                                    qdio_call_shutdown);
                        queue_work(ccw_device_work, &cdev->private->kick_work);
                }
                break;
@@ -3458,19 +3451,18 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
        struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr;
 
        /* This is the outbound handling of queues */
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.start_time_outbound=NOW;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats)
+               perf_stats.start_time_outbound=NOW;
 
        qdio_do_qdio_fill_output(q,qidx,count,buffers);
 
        used_elements=atomic_add_return(count, &q->number_of_buffers_used) - count;
 
        if (callflags&QDIO_FLAG_DONT_SIGA) {
-#ifdef QDIO_PERFORMANCE_STATS
-               perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
-               perf_stats.outbound_cnt++;
-#endif /* QDIO_PERFORMANCE_STATS */
+               if (qdio_performance_stats) {
+                       perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
+                       perf_stats.outbound_cnt++;
+               }
                return;
        }
        if (q->is_iqdio_q) {
@@ -3500,9 +3492,8 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
                                qdio_kick_outbound_q(q);
                        } else {
                                QDIO_DBF_TEXT3(0,trace, "fast-req");
-#ifdef QDIO_PERFORMANCE_STATS
-                               perf_stats.fast_reqs++;
-#endif /* QDIO_PERFORMANCE_STATS */
+                               if (qdio_performance_stats)
+                                       perf_stats.fast_reqs++;
                        }
                }
                /* 
@@ -3513,10 +3504,10 @@ do_qdio_handle_outbound(struct qdio_q *q, unsigned int callflags,
                __qdio_outbound_processing(q);
        }
 
-#ifdef QDIO_PERFORMANCE_STATS
-       perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
-       perf_stats.outbound_cnt++;
-#endif /* QDIO_PERFORMANCE_STATS */
+       if (qdio_performance_stats) {
+               perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
+               perf_stats.outbound_cnt++;
+       }
 }
 
 /* count must be 1 in iqdio */
@@ -3574,7 +3565,6 @@ do_QDIO(struct ccw_device *cdev,unsigned int callflags,
        return 0;
 }
 
-#ifdef QDIO_PERFORMANCE_STATS
 static int
 qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
                        int buffer_length, int *eof, void *data)
@@ -3590,29 +3580,29 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
        _OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c);
        _OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c);
        _OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c);
-       _OUTP_IT("Number of tasklet runs (total)                  : %u\n",
+       _OUTP_IT("Number of tasklet runs (total)                  : %lu\n",
                 perf_stats.tl_runs);
        _OUTP_IT("\n");
-       _OUTP_IT("Number of SIGA sync's issued                    : %u\n",
+       _OUTP_IT("Number of SIGA sync's issued                    : %lu\n",
                 perf_stats.siga_syncs);
-       _OUTP_IT("Number of SIGA in's issued                      : %u\n",
+       _OUTP_IT("Number of SIGA in's issued                      : %lu\n",
                 perf_stats.siga_ins);
-       _OUTP_IT("Number of SIGA out's issued                     : %u\n",
+       _OUTP_IT("Number of SIGA out's issued                     : %lu\n",
                 perf_stats.siga_outs);
-       _OUTP_IT("Number of PCIs caught                           : %u\n",
+       _OUTP_IT("Number of PCIs caught                           : %lu\n",
                 perf_stats.pcis);
-       _OUTP_IT("Number of adapter interrupts caught             : %u\n",
+       _OUTP_IT("Number of adapter interrupts caught             : %lu\n",
                 perf_stats.thinints);
-       _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %u\n",
+       _OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %lu\n",
                 perf_stats.fast_reqs);
        _OUTP_IT("\n");
-       _OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n",
+       _OUTP_IT("Total time of all inbound actions (us) incl. UL : %lu\n",
                 perf_stats.inbound_time);
-       _OUTP_IT("Number of inbound transfers                     : %u\n",
+       _OUTP_IT("Number of inbound transfers                     : %lu\n",
                 perf_stats.inbound_cnt);
-       _OUTP_IT("Total time of all outbound do_QDIOs (us)        : %u\n",
+       _OUTP_IT("Total time of all outbound do_QDIOs (us)        : %lu\n",
                 perf_stats.outbound_time);
-       _OUTP_IT("Number of do_QDIOs outbound                     : %u\n",
+       _OUTP_IT("Number of do_QDIOs outbound                     : %lu\n",
                 perf_stats.outbound_cnt);
        _OUTP_IT("\n");
 
@@ -3620,12 +3610,10 @@ qdio_perf_procfile_read(char *buffer, char **buffer_location, off_t offset,
 }
 
 static struct proc_dir_entry *qdio_perf_proc_file;
-#endif /* QDIO_PERFORMANCE_STATS */
 
 static void
 qdio_add_procfs_entry(void)
 {
-#ifdef QDIO_PERFORMANCE_STATS
         proc_perf_file_registration=0;
        qdio_perf_proc_file=create_proc_entry(QDIO_PERF,
                                              S_IFREG|0444,&proc_root);
@@ -3637,20 +3625,58 @@ qdio_add_procfs_entry(void)
                 QDIO_PRINT_WARN("was not able to register perf. " \
                                "proc-file (%i).\n",
                                proc_perf_file_registration);
-#endif /* QDIO_PERFORMANCE_STATS */
 }
 
 static void
 qdio_remove_procfs_entry(void)
 {
-#ifdef QDIO_PERFORMANCE_STATS
        perf_stats.tl_runs=0;
 
         if (!proc_perf_file_registration) /* means if it went ok earlier */
                remove_proc_entry(QDIO_PERF,&proc_root);
-#endif /* QDIO_PERFORMANCE_STATS */
 }
 
+/**
+ * attributes in sysfs
+ *****************************************************************************/
+
+static ssize_t
+qdio_performance_stats_show(struct bus_type *bus, char *buf)
+{
+       return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0);
+}
+
+static ssize_t
+qdio_performance_stats_store(struct bus_type *bus, const char *buf, size_t count)
+{
+       char *tmp;
+       int i;
+
+       i = simple_strtoul(buf, &tmp, 16);
+       if ((i == 0) || (i == 1)) {
+               if (i == qdio_performance_stats)
+                       return count;
+               qdio_performance_stats = i;
+               if (i==0) {
+                       /* reset perf. stat. info */
+                       i_p_nc = 0;
+                       i_p_c = 0;
+                       ii_p_nc = 0;
+                       ii_p_c = 0;
+                       o_p_nc = 0;
+                       o_p_c = 0;
+                       memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
+               }
+       } else {
+               QDIO_PRINT_WARN("QDIO performance_stats: write 0 or 1 to this file!\n");
+               return -EINVAL;
+       }
+       return count;
+}
+
+static BUS_ATTR(qdio_performance_stats, 0644, qdio_performance_stats_show,
+                       qdio_performance_stats_store);
+
 static void
 tiqdio_register_thinints(void)
 {
@@ -3695,6 +3721,7 @@ qdio_release_qdio_memory(void)
        kfree(indicators);
 }
 
+
 static void
 qdio_unregister_dbf_views(void)
 {
@@ -3796,9 +3823,7 @@ static int __init
 init_QDIO(void)
 {
        int res;
-#ifdef QDIO_PERFORMANCE_STATS
        void *ptr;
-#endif /* QDIO_PERFORMANCE_STATS */
 
        printk("qdio: loading %s\n",version);
 
@@ -3811,13 +3836,12 @@ init_QDIO(void)
                return res;
 
        QDIO_DBF_TEXT0(0,setup,"initQDIO");
+       res = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
 
-#ifdef QDIO_PERFORMANCE_STATS
-               memset((void*)&perf_stats,0,sizeof(perf_stats));
+       memset((void*)&perf_stats,0,sizeof(perf_stats));
        QDIO_DBF_TEXT0(0,setup,"perfstat");
        ptr=&perf_stats;
        QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*));
-#endif /* QDIO_PERFORMANCE_STATS */
 
        qdio_add_procfs_entry();
 
@@ -3841,7 +3865,7 @@ cleanup_QDIO(void)
        qdio_release_qdio_memory();
        qdio_unregister_dbf_views();
        mempool_destroy(qdio_mempool_scssc);
-
+       bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
        printk("qdio: %s: module removed\n",version);
 }
 
index 42927c1..ec9af72 100644 (file)
 #endif /* CONFIG_QDIO_DEBUG */
 #define QDIO_USE_PROCESSING_STATE
 
-#ifdef CONFIG_QDIO_PERF_STATS
-#define QDIO_PERFORMANCE_STATS
-#endif /* CONFIG_QDIO_PERF_STATS */
-
 #define QDIO_MINIMAL_BH_RELIEF_TIME 16
 #define QDIO_TIMER_POLL_VALUE 1
 #define IQDIO_TIMER_POLL_VALUE 1
@@ -409,25 +405,23 @@ do_clear_global_summary(void)
 #define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
 #define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
 
-#ifdef QDIO_PERFORMANCE_STATS
 struct qdio_perf_stats {
-       unsigned int tl_runs;
+       unsigned long tl_runs;
 
-       unsigned int siga_outs;
-       unsigned int siga_ins;
-       unsigned int siga_syncs;
-       unsigned int pcis;
-       unsigned int thinints;
-       unsigned int fast_reqs;
+       unsigned long siga_outs;
+       unsigned long siga_ins;
+       unsigned long siga_syncs;
+       unsigned long pcis;
+       unsigned long thinints;
+       unsigned long fast_reqs;
 
        __u64 start_time_outbound;
-       unsigned int outbound_cnt;
-       unsigned int outbound_time;
+       unsigned long outbound_cnt;
+       unsigned long outbound_time;
        __u64 start_time_inbound;
-       unsigned int inbound_cnt;
-       unsigned int inbound_time;
+       unsigned long inbound_cnt;
+       unsigned long inbound_time;
 };
-#endif /* QDIO_PERFORMANCE_STATS */
 
 /* unlikely as the later the better */
 #define SYNC_MEMORY if (unlikely(q->siga_sync)) qdio_siga_sync_q(q)
index e4dc947..ad60afe 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <asm/s390_rdev.h>
+#include <asm/reset.h>
 
 #include "ap_bus.h"
 
@@ -1128,6 +1129,19 @@ static void ap_poll_thread_stop(void)
        mutex_unlock(&ap_poll_thread_mutex);
 }
 
+static void ap_reset(void)
+{
+       int i, j;
+
+       for (i = 0; i < AP_DOMAINS; i++)
+               for (j = 0; j < AP_DEVICES; j++)
+                       ap_reset_queue(AP_MKQID(j, i));
+}
+
+static struct reset_call ap_reset_call = {
+       .fn = ap_reset,
+};
+
 /**
  * The module initialization code.
  */
@@ -1144,6 +1158,7 @@ int __init ap_module_init(void)
                printk(KERN_WARNING "AP instructions not installed.\n");
                return -ENODEV;
        }
+       register_reset_call(&ap_reset_call);
 
        /* Create /sys/bus/ap. */
        rc = bus_register(&ap_bus_type);
@@ -1197,6 +1212,7 @@ out_bus:
                bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
        bus_unregister(&ap_bus_type);
 out:
+       unregister_reset_call(&ap_reset_call);
        return rc;
 }
 
@@ -1227,6 +1243,7 @@ void ap_module_exit(void)
        for (i = 0; ap_bus_attrs[i]; i++)
                bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
        bus_unregister(&ap_bus_type);
+       unregister_reset_call(&ap_reset_call);
 }
 
 #ifndef CONFIG_ZCRYPT_MONOLITHIC
index c042f95..604f68f 100644 (file)
@@ -69,11 +69,13 @@ typedef struct dasd_information2_t {
  * 0x01: readonly (ro)
  * 0x02: use diag discipline (diag)
  * 0x04: set the device initially online (internal use only)
+ * 0x08: enable ERP related logging
  */
 #define DASD_FEATURE_DEFAULT        0x00
 #define DASD_FEATURE_READONLY       0x01
 #define DASD_FEATURE_USEDIAG        0x02
 #define DASD_FEATURE_INITIAL_ONLINE  0x04
+#define DASD_FEATURE_ERPLOG         0x08
 
 #define DASD_PARTN_BITS 2
 
index 363ea76..05ea6f1 100644 (file)
@@ -127,6 +127,26 @@ page_get_storage_key(unsigned long addr)
        return skey;
 }
 
+extern unsigned long max_pfn;
+
+static inline int pfn_valid(unsigned long pfn)
+{
+       unsigned long dummy;
+       int ccode;
+
+       if (pfn >= max_pfn)
+               return 0;
+
+       asm volatile(
+               "       lra     %0,0(%2)\n"
+               "       ipm     %1\n"
+               "       srl     %1,28\n"
+               : "=d" (dummy), "=d" (ccode)
+               : "a" (pfn << PAGE_SHIFT)
+               : "cc");
+       return !ccode;
+}
+
 #endif /* !__ASSEMBLY__ */
 
 /* to align the pointer to the (next) page boundary */
@@ -138,8 +158,6 @@ page_get_storage_key(unsigned long addr)
 #define __va(x)                 (void *)(unsigned long)(x)
 #define virt_to_page(kaddr)    pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
 #define page_to_phys(page)     (page_to_pfn(page) << PAGE_SHIFT)
-
-#define pfn_valid(pfn)         ((pfn) < max_mapnr)
 #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 
 #define VM_DATA_DEFAULT_FLAGS  (VM_READ | VM_WRITE | VM_EXEC | \
index 28619de..0707a7e 100644 (file)
@@ -25,8 +25,11 @@ extern void diag10(unsigned long addr);
  * Page allocation orders.
  */
 #ifndef __s390x__
+# define PTE_ALLOC_ORDER       0
+# define PMD_ALLOC_ORDER       0
 # define PGD_ALLOC_ORDER       1
 #else /* __s390x__ */
+# define PTE_ALLOC_ORDER       0
 # define PMD_ALLOC_ORDER       2
 # define PGD_ALLOC_ORDER       2
 #endif /* __s390x__ */
index 2d968a6..ae61aca 100644 (file)
@@ -107,23 +107,25 @@ extern char empty_zero_page[PAGE_SIZE];
  * The vmalloc() routines leaves a hole of 4kB between each vmalloced
  * area for the same reason. ;)
  */
+extern unsigned long vmalloc_end;
 #define VMALLOC_OFFSET  (8*1024*1024)
 #define VMALLOC_START   (((unsigned long) high_memory + VMALLOC_OFFSET) \
                         & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_END    vmalloc_end
 
 /*
  * We need some free virtual space to be able to do vmalloc.
  * VMALLOC_MIN_SIZE defines the minimum size of the vmalloc
  * area. On a machine with 2GB memory we make sure that we
  * have at least 128MB free space for vmalloc. On a machine
- * with 4TB we make sure we have at least 1GB.
+ * with 4TB we make sure we have at least 128GB.
  */
 #ifndef __s390x__
 #define VMALLOC_MIN_SIZE       0x8000000UL
-#define VMALLOC_END            0x80000000UL
+#define VMALLOC_END_INIT       0x80000000UL
 #else /* __s390x__ */
-#define VMALLOC_MIN_SIZE       0x40000000UL
-#define VMALLOC_END            0x40000000000UL
+#define VMALLOC_MIN_SIZE       0x2000000000UL
+#define VMALLOC_END_INIT       0x40000000000UL
 #endif /* __s390x__ */
 
 /*
@@ -815,11 +817,17 @@ static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
 
 #define kern_addr_valid(addr)   (1)
 
+extern int add_shared_memory(unsigned long start, unsigned long size);
+extern int remove_shared_memory(unsigned long start, unsigned long size);
+
 /*
  * No page table caches to initialise
  */
 #define pgtable_cache_init()   do { } while (0)
 
+#define __HAVE_ARCH_MEMMAP_INIT
+extern void memmap_init(unsigned long, int, unsigned long, unsigned long);
+
 #define __HAVE_ARCH_PTEP_ESTABLISH
 #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
 #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG