diff options
219 files changed, 10329 insertions, 4077 deletions
diff --git a/Documentation/ABI/testing/sysfs-block b/Documentation/ABI/testing/sysfs-block index 4bd9ea53912..44f52a4f590 100644 --- a/Documentation/ABI/testing/sysfs-block +++ b/Documentation/ABI/testing/sysfs-block @@ -26,3 +26,37 @@ Description: I/O statistics of partition <part>. The format is the same as the above-written /sys/block/<disk>/stat format. + + +What: /sys/block/<disk>/integrity/format +Date: June 2008 +Contact: Martin K. Petersen <martin.petersen@oracle.com> +Description: + Metadata format for integrity capable block device. + E.g. T10-DIF-TYPE1-CRC. + + +What: /sys/block/<disk>/integrity/read_verify +Date: June 2008 +Contact: Martin K. Petersen <martin.petersen@oracle.com> +Description: + Indicates whether the block layer should verify the + integrity of read requests serviced by devices that + support sending integrity metadata. + + +What: /sys/block/<disk>/integrity/tag_size +Date: June 2008 +Contact: Martin K. Petersen <martin.petersen@oracle.com> +Description: + Number of bytes of integrity tag space available per + 512 bytes of data. + + +What: /sys/block/<disk>/integrity/write_generate +Date: June 2008 +Contact: Martin K. Petersen <martin.petersen@oracle.com> +Description: + Indicates whether the block layer should automatically + generate checksums for write requests bound for + devices that support receiving integrity metadata. diff --git a/Documentation/ABI/testing/sysfs-bus-css b/Documentation/ABI/testing/sysfs-bus-css new file mode 100644 index 00000000000..b585ec258a0 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-css @@ -0,0 +1,35 @@ +What: /sys/bus/css/devices/.../type +Date: March 2008 +Contact: Cornelia Huck <cornelia.huck@de.ibm.com> + linux-s390@vger.kernel.org +Description: Contains the subchannel type, as reported by the hardware. + This attribute is present for all subchannel types. + +What: /sys/bus/css/devices/.../modalias +Date: March 2008 +Contact: Cornelia Huck <cornelia.huck@de.ibm.com> + linux-s390@vger.kernel.org +Description: Contains the module alias as reported with uevents. + It is of the format css:t<type> and present for all + subchannel types. + +What: /sys/bus/css/drivers/io_subchannel/.../chpids +Date: December 2002 +Contact: Cornelia Huck <cornelia.huck@de.ibm.com> + linux-s390@vger.kernel.org +Description: Contains the ids of the channel paths used by this + subchannel, as reported by the channel subsystem + during subchannel recognition. + Note: This is an I/O-subchannel specific attribute. +Users: s390-tools, HAL + +What: /sys/bus/css/drivers/io_subchannel/.../pimpampom +Date: December 2002 +Contact: Cornelia Huck <cornelia.huck@de.ibm.com> + linux-s390@vger.kernel.org +Description: Contains the PIM/PAM/POM values, as reported by the + channel subsystem when last queried by the common I/O + layer (this implies that this attribute is not neccessarily + in sync with the values current in the channel subsystem). + Note: This is an I/O-subchannel specific attribute. +Users: s390-tools, HAL diff --git a/Documentation/block/data-integrity.txt b/Documentation/block/data-integrity.txt new file mode 100644 index 00000000000..e9dc8d86adc --- /dev/null +++ b/Documentation/block/data-integrity.txt @@ -0,0 +1,327 @@ +---------------------------------------------------------------------- +1. INTRODUCTION + +Modern filesystems feature checksumming of data and metadata to +protect against data corruption. However, the detection of the +corruption is done at read time which could potentially be months +after the data was written. At that point the original data that the +application tried to write is most likely lost. + +The solution is to ensure that the disk is actually storing what the +application meant it to. Recent additions to both the SCSI family +protocols (SBC Data Integrity Field, SCC protection proposal) as well +as SATA/T13 (External Path Protection) try to remedy this by adding +support for appending integrity metadata to an I/O. The integrity +metadata (or protection information in SCSI terminology) includes a +checksum for each sector as well as an incrementing counter that +ensures the individual sectors are written in the right order. And +for some protection schemes also that the I/O is written to the right +place on disk. + +Current storage controllers and devices implement various protective +measures, for instance checksumming and scrubbing. But these +technologies are working in their own isolated domains or at best +between adjacent nodes in the I/O path. The interesting thing about +DIF and the other integrity extensions is that the protection format +is well defined and every node in the I/O path can verify the +integrity of the I/O and reject it if corruption is detected. This +allows not only corruption prevention but also isolation of the point +of failure. + +---------------------------------------------------------------------- +2. THE DATA INTEGRITY EXTENSIONS + +As written, the protocol extensions only protect the path between +controller and storage device. However, many controllers actually +allow the operating system to interact with the integrity metadata +(IMD). We have been working with several FC/SAS HBA vendors to enable +the protection information to be transferred to and from their +controllers. + +The SCSI Data Integrity Field works by appending 8 bytes of protection +information to each sector. The data + integrity metadata is stored +in 520 byte sectors on disk. Data + IMD are interleaved when +transferred between the controller and target. The T13 proposal is +similar. + +Because it is highly inconvenient for operating systems to deal with +520 (and 4104) byte sectors, we approached several HBA vendors and +encouraged them to allow separation of the data and integrity metadata +scatter-gather lists. + +The controller will interleave the buffers on write and split them on +read. This means that the Linux can DMA the data buffers to and from +host memory without changes to the page cache. + +Also, the 16-bit CRC checksum mandated by both the SCSI and SATA specs +is somewhat heavy to compute in software. Benchmarks found that +calculating this checksum had a significant impact on system +performance for a number of workloads. Some controllers allow a +lighter-weight checksum to be used when interfacing with the operating +system. Emulex, for instance, supports the TCP/IP checksum instead. +The IP checksum received from the OS is converted to the 16-bit CRC +when writing and vice versa. This allows the integrity metadata to be +generated by Linux or the application at very low cost (comparable to +software RAID5). + +The IP checksum is weaker than the CRC in terms of detecting bit +errors. However, the strength is really in the separation of the data +buffers and the integrity metadata. These two distinct buffers much +match up for an I/O to complete. + +The separation of the data and integrity metadata buffers as well as +the choice in checksums is referred to as the Data Integrity +Extensions. As these extensions are outside the scope of the protocol +bodies (T10, T13), Oracle and its partners are trying to standardize +them within the Storage Networking Industry Association. + +---------------------------------------------------------------------- +3. KERNEL CHANGES + +The data integrity framework in Linux enables protection information +to be pinned to I/Os and sent to/received from controllers that +support it. + +The advantage to the integrity extensions in SCSI and SATA is that +they enable us to protect the entire path from application to storage +device. However, at the same time this is also the biggest +disadvantage. It means that the protection information must be in a +format that can be understood by the disk. + +Generally Linux/POSIX applications are agnostic to the intricacies of +the storage devices they are accessing. The virtual filesystem switch +and the block layer make things like hardware sector size and +transport protocols completely transparent to the application. + +However, this level of detail is required when preparing the +protection information to send to a disk. Consequently, the very +concept of an end-to-end protection scheme is a layering violation. +It is completely unreasonable for an application to be aware whether +it is accessing a SCSI or SATA disk. + +The data integrity support implemented in Linux attempts to hide this +from the application. As far as the application (and to some extent +the kernel) is concerned, the integrity metadata is opaque information +that's attached to the I/O. + +The current implementation allows the block layer to automatically +generate the protection information for any I/O. Eventually the +intent is to move the integrity metadata calculation to userspace for +user data. Metadata and other I/O that originates within the kernel +will still use the automatic generation interface. + +Some storage devices allow each hardware sector to be tagged with a +16-bit value. The owner of this tag space is the owner of the block +device. I.e. the filesystem in most cases. The filesystem can use +this extra space to tag sectors as they see fit. Because the tag +space is limited, the block interface allows tagging bigger chunks by +way of interleaving. This way, 8*16 bits of information can be +attached to a typical 4KB filesystem block. + +This also means that applications such as fsck and mkfs will need +access to manipulate the tags from user space. A passthrough +interface for this is being worked on. + + +---------------------------------------------------------------------- +4. BLOCK LAYER IMPLEMENTATION DETAILS + +4.1 BIO + +The data integrity patches add a new field to struct bio when +CONFIG_BLK_DEV_INTEGRITY is enabled. bio->bi_integrity is a pointer +to a struct bip which contains the bio integrity payload. Essentially +a bip is a trimmed down struct bio which holds a bio_vec containing +the integrity metadata and the required housekeeping information (bvec +pool, vector count, etc.) + +A kernel subsystem can enable data integrity protection on a bio by +calling bio_integrity_alloc(bio). This will allocate and attach the +bip to the bio. + +Individual pages containing integrity metadata can subsequently be +attached using bio_integrity_add_page(). + +bio_free() will automatically free the bip. + + +4.2 BLOCK DEVICE + +Because the format of the protection data is tied to the physical +disk, each block device has been extended with a block integrity +profile (struct blk_integrity). This optional profile is registered +with the block layer using blk_integrity_register(). + +The profile contains callback functions for generating and verifying +the protection data, as well as getting and setting application tags. +The profile also contains a few constants to aid in completing, +merging and splitting the integrity metadata. + +Layered block devices will need to pick a profile that's appropriate +for all subdevices. blk_integrity_compare() can help with that. DM +and MD linear, RAID0 and RAID1 are currently supported. RAID4/5/6 +will require extra work due to the application tag. + + +---------------------------------------------------------------------- +5.0 BLOCK LAYER INTEGRITY API + +5.1 NORMAL FILESYSTEM + + The normal filesystem is unaware that the underlying block device + is capable of sending/receiving integrity metadata. The IMD will + be automatically generated by the block layer at submit_bio() time + in case of a WRITE. A READ request will cause the I/O integrity + to be verified upon completion. + + IMD generation and verification can be toggled using the + + /sys/block/<bdev>/integrity/write_generate + + and + + /sys/block/<bdev>/integrity/read_verify + + flags. + + +5.2 INTEGRITY-AWARE FILESYSTEM + + A filesystem that is integrity-aware can prepare I/Os with IMD + attached. It can also use the application tag space if this is + supported by the block device. + + + int bdev_integrity_enabled(block_device, int rw); + + bdev_integrity_enabled() will return 1 if the block device + supports integrity metadata transfer for the data direction + specified in 'rw'. + + bdev_integrity_enabled() honors the write_generate and + read_verify flags in sysfs and will respond accordingly. + + + int bio_integrity_prep(bio); + + To generate IMD for WRITE and to set up buffers for READ, the + filesystem must call bio_integrity_prep(bio). + + Prior to calling this function, the bio data direction and start + sector must be set, and the bio should have all data pages + added. It is up to the caller to ensure that the bio does not + change while I/O is in progress. + + bio_integrity_prep() should only be called if + bio_integrity_enabled() returned 1. + + + int bio_integrity_tag_size(bio); + + If the filesystem wants to use the application tag space it will + first have to find out how much storage space is available. + Because tag space is generally limited (usually 2 bytes per + sector regardless of sector size), the integrity framework + supports interleaving the information between the sectors in an + I/O. + + Filesystems can call bio_integrity_tag_size(bio) to find out how + many bytes of storage are available for that particular bio. + + Another option is bdev_get_tag_size(block_device) which will + return the number of available bytes per hardware sector. + + + int bio_integrity_set_tag(bio, void *tag_buf, len); + + After a successful return from bio_integrity_prep(), + bio_integrity_set_tag() can be used to attach an opaque tag + buffer to a bio. Obviously this only makes sense if the I/O is + a WRITE. + + + int bio_integrity_get_tag(bio, void *tag_buf, len); + + Similarly, at READ I/O completion time the filesystem can + retrieve the tag buffer using bio_integrity_get_tag(). + + +6.3 PASSING EXISTING INTEGRITY METADATA + + Filesystems that either generate their own integrity metadata or + are capable of transferring IMD from user space can use the + following calls: + + + struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages); + + Allocates the bio integrity payload and hangs it off of the bio. + nr_pages indicate how many pages of protection data need to be + stored in the integrity bio_vec list (similar to bio_alloc()). + + The integrity payload will be freed at bio_free() time. + + + int bio_integrity_add_page(bio, page, len, offset); + + Attaches a page containing integrity metadata to an existing + bio. The bio must have an existing bip, + i.e. bio_integrity_alloc() must have been called. For a WRITE, + the integrity metadata in the pages must be in a format + understood by the target device with the notable exception that + the sector numbers will be remapped as the request traverses the + I/O stack. This implies that the pages added using this call + will be modified during I/O! The first reference tag in the + integrity metadata must have a value of bip->bip_sector. + + Pages can be added using bio_integrity_add_page() as long as + there is room in the bip bio_vec array (nr_pages). + + Upon completion of a READ operation, the attached pages will + contain the integrity metadata received from the storage device. + It is up to the receiver to process them and verify data + integrity upon completion. + + +6.4 REGISTERING A BLOCK DEVICE AS CAPABLE OF EXCHANGING INTEGRITY + METADATA + + To enable integrity exchange on a block device the gendisk must be + registered as capable: + + int blk_integrity_register(gendisk, blk_integrity); + + The blk_integrity struct is a template and should contain the + following: + + static struct blk_integrity my_profile = { + .name = "STANDARDSBODY-TYPE-VARIANT-CSUM", + .generate_fn = my_generate_fn, + .verify_fn = my_verify_fn, + .get_tag_fn = my_get_tag_fn, + .set_tag_fn = my_set_tag_fn, + .tuple_size = sizeof(struct my_tuple_size), + .tag_size = <tag bytes per hw sector>, + }; + + 'name' is a text string which will be visible in sysfs. This is + part of the userland API so chose it carefully and never change + it. The format is standards body-type-variant. + E.g. T10-DIF-TYPE1-IP or T13-EPP-0-CRC. + + 'generate_fn' generates appropriate integrity metadata (for WRITE). + + 'verify_fn' verifies that the data buffer matches the integrity + metadata. + + 'tuple_size' must be set to match the size of the integrity + metadata per sector. I.e. 8 for DIF and EPP. + + 'tag_size' must be set to identify how many bytes of tag space + are available per hardware sector. For DIF this is either 2 or + 0 depending on the value of the Control Mode Page ATO bit. + + See 6.2 for a description of get_tag_fn and set_tag_fn. + +---------------------------------------------------------------------- +2007-12-24 Martin K. Petersen <martin.petersen@oracle.com> diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl-number.txt index 240ce7a56c4..3bb5f466a90 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl-number.txt @@ -117,6 +117,7 @@ Code Seq# Include File Comments <mailto:natalia@nikhefk.nikhef.nl> 'c' 00-7F linux/comstats.h conflict! 'c' 00-7F linux/coda.h conflict! +'c' 80-9F asm-s390/chsc.h 'd' 00-FF linux/char/drm/drm/h conflict! 'd' 00-DF linux/video_decoder.h conflict! 'd' F0-FF linux/digi1.h diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 107e492cb47..5dc8f8028d5 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -146,6 +146,7 @@ config MATHEMU config COMPAT bool "Kernel support for 31 bit emulation" depends on 64BIT + select COMPAT_BINFMT_ELF help Select this option if you want to enable your system kernel to handle system-calls from ELF binaries for 31 bit ESA. This option @@ -312,6 +313,10 @@ config ARCH_SPARSEMEM_DEFAULT config ARCH_SELECT_MEMORY_MODEL def_bool y +config ARCH_ENABLE_MEMORY_HOTPLUG + def_bool y + depends on SPARSEMEM + source "mm/Kconfig" comment "I/O subsystem configuration" @@ -344,6 +349,22 @@ config QDIO_DEBUG If unsure, say N. +config CHSC_SCH + tristate "Support for CHSC subchannels" + help + This driver allows usage of CHSC subchannels. A CHSC subchannel + is usually present on LPAR only. + The driver creates a device /dev/chsc, which may be used to + obtain I/O configuration information about the machine and + to issue asynchronous chsc commands (DANGEROUS). + You will usually only want to use this interface on a special + LPAR designated for system management. + + To compile this driver as a module, choose M here: the + module will be called chsc_sch. + + If unsure, say N. + comment "Misc" config IPL diff --git a/arch/s390/appldata/appldata.h b/arch/s390/appldata/appldata.h index db3ae850510..17a2636fec0 100644 --- a/arch/s390/appldata/appldata.h +++ b/arch/s390/appldata/appldata.h @@ -3,13 +3,11 @@ * * Definitions and interface for Linux - z/VM Monitor Stream. * - * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * Copyright IBM Corp. 2003, 2008 * * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */ -//#define APPLDATA_DEBUG /* Debug messages on/off */ - #define APPLDATA_MAX_REC_SIZE 4024 /* Maximum size of the */ /* data buffer */ #define APPLDATA_MAX_PROCS 100 @@ -32,12 +30,6 @@ #define P_ERROR(x...) printk(KERN_ERR MY_PRINT_NAME " error: " x) #define P_WARNING(x...) printk(KERN_WARNING MY_PRINT_NAME " status: " x) -#ifdef APPLDATA_DEBUG -#define P_DEBUG(x...) printk(KERN_DEBUG MY_PRINT_NAME " debug: " x) -#else -#define P_DEBUG(x...) do {} while (0) -#endif - struct appldata_ops { struct list_head list; struct ctl_table_header *sysctl_header; diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index ad40729bec3..9cb3d92447a 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -5,7 +5,7 @@ * Exports appldata_register_ops() and appldata_unregister_ops() for the * data gathering modules. * - * Copyright (C) 2003,2006 IBM Corporation, IBM Deutschland Entwicklung GmbH. + * Copyright IBM Corp. 2003, 2008 * * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */ @@ -108,9 +108,6 @@ static LIST_HEAD(appldata_ops_list); */ static void appldata_timer_function(unsigned long data) { - P_DEBUG(" -= Timer =-\n"); - P_DEBUG("CPU: %i, expire_count: %i\n", smp_processor_id(), - atomic_read(&appldata_expire_count)); if (atomic_dec_and_test(&appldata_expire_count)) { atomic_set(&appldata_expire_count, num_online_cpus()); queue_work(appldata_wq, (struct work_struct *) data); @@ -128,14 +125,11 @@ static void appldata_work_fn(struct work_struct *work) struct appldata_ops *ops; int i; - P_DEBUG(" -= Work Queue =-\n"); i = 0; get_online_cpus(); spin_lock(&appldata_ops_lock); list_for_each(lh, &appldata_ops_list) { ops = list_entry(lh, struct appldata_ops, list); - P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n", - ++i, ops->active, ops->name); if (ops->active == 1) { ops->callback(ops->data); } @@ -212,7 +206,6 @@ __appldata_vtimer_setup(int cmd) 0, 1); } appldata_timer_active = 1; - P_INFO("Monitoring timer started.\n"); break; case APPLDATA_DEL_TIMER: for_each_online_cpu(i) @@ -221,7 +214,6 @@ __appldata_vtimer_setup(int cmd) break; appldata_timer_active = 0; atomic_set(&appldata_expire_count, num_online_cpus()); - P_INFO("Monitoring timer stopped.\n"); break; case APPLDATA_MOD_TIMER: per_cpu_interval = (u64) (appldata_interval*1000 / @@ -313,10 +305,8 @@ appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, } interval = 0; sscanf(buf, "%i", &interval); - if (interval <= 0) { - P_ERROR("Timer CPU interval has to be > 0!\n"); + if (interval <= 0) return -EINVAL; - } get_online_cpus(); spin_lock(&appldata_timer_lock); @@ -324,9 +314,6 @@ appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, __appldata_vtimer_setup(APPLDATA_MOD_TIMER); spin_unlock(&appldata_timer_lock); put_online_cpus(); - - P_INFO("Monitoring CPU interval set to %u milliseconds.\n", - interval); out: *lenp = len; *ppos += len; @@ -406,23 +393,16 @@ appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, P_ERROR("START DIAG 0xDC for %s failed, " "return code: %d\n", ops->name, rc); module_put(ops->owner); - } else { - P_INFO("Monitoring %s data enabled, " - "DIAG 0xDC started.\n", ops->name); + } else ops->active = 1; - } } else if ((buf[0] == '0') && (ops->active == 1)) { ops->active = 0; rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC, (unsigned long) ops->data, ops->size, ops->mod_lvl); - if (rc != 0) { + if (rc != 0) P_ERROR("STOP DIAG 0xDC for %s failed, " "return code: %d\n", ops->name, rc); - } else { - P_INFO("Monitoring %s data disabled, " - "DIAG 0xDC stopped.\n", ops->name); - } module_put(ops->owner); } spin_unlock(&appldata_ops_lock); @@ -468,7 +448,6 @@ int appldata_register_ops(struct appldata_ops *ops) ops->sysctl_header = register_sysctl_table(ops->ctl_table); if (!ops->sysctl_header) goto out; - P_INFO("%s-ops registered!\n", ops->name); return 0; out: spin_lock(&appldata_ops_lock); @@ -490,7 +469,6 @@ void appldata_unregister_ops(struct appldata_ops *ops) spin_unlock(&appldata_ops_lock); unregister_sysctl_table(ops->sysctl_header); kfree(ops->ctl_table); - P_INFO("%s-ops unregistered!\n", ops->name); } /********************** module-ops management <END> **************************/ @@ -553,14 +531,9 @@ static int __init appldata_init(void) { int i; - P_DEBUG("sizeof(parameter_list) = %lu\n", - sizeof(struct appldata_parameter_list)); - appldata_wq = create_singlethread_workqueue("appldata"); - if (!appldata_wq) { - P_ERROR("Could not create work queue\n"); + if (!appldata_wq) return -ENOMEM; - } get_online_cpus(); for_each_online_cpu(i) @@ -571,8 +544,6 @@ static int __init appldata_init(void) register_hotcpu_notifier(&appldata_nb); appldata_sysctl_header = register_sysctl_table(appldata_dir_table); - - P_DEBUG("Base interface initialized.\n"); return 0; } @@ -584,7 +555,9 @@ EXPORT_SYMBOL_GPL(appldata_register_ops); EXPORT_SYMBOL_GPL(appldata_unregister_ops); EXPORT_SYMBOL_GPL(appldata_diag); +#ifdef CONFIG_SWAP EXPORT_SYMBOL_GPL(si_swapinfo); +#endif EXPORT_SYMBOL_GPL(nr_threads); EXPORT_SYMBOL_GPL(nr_running); EXPORT_SYMBOL_GPL(nr_iowait); diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index 51181ccdb87..3ed56b7d1b2 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c @@ -14,14 +14,13 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/kernel_stat.h> -#include <asm/io.h> #include <linux/pagemap.h> #include <linux/swap.h> +#include <asm/io.h> #include "appldata.h" -#define MY_PRINT_NAME "appldata_mem" /* for debug messages, etc. */ #define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* Converts #Pages to KB */ /* @@ -70,30 +69,6 @@ static struct appldata_mem_data { } __attribute__((packed)) appldata_mem_data; -static inline void appldata_debug_print(struct appldata_mem_data *mem_data) -{ - P_DEBUG("--- MEM - RECORD ---\n"); - P_DEBUG("pgpgin = %8lu KB\n", mem_data->pgpgin); - P_DEBUG("pgpgout = %8lu KB\n", mem_data->pgpgout); - P_DEBUG("pswpin = %8lu Pages\n", mem_data->pswpin); - P_DEBUG("pswpout = %8lu Pages\n", mem_data->pswpout); - P_DEBUG("pgalloc = %8lu \n", mem_data->pgalloc); - P_DEBUG("pgfault = %8lu \n", mem_data->pgfault); - P_DEBUG("pgmajfault = %8lu \n", mem_data->pgmajfault); - P_DEBUG("sharedram = %8lu KB\n", mem_data->sharedram); - P_DEBUG("totalram = %8lu KB\n", mem_data->totalram); - P_DEBUG("freeram = %8lu KB\n", mem_data->freeram); - P_DEBUG("totalhigh = %8lu KB\n", mem_data->totalhigh); - P_DEBUG("freehigh = %8lu KB\n", mem_data->freehigh); - P_DEBUG("bufferram = %8lu KB\n", mem_data->bufferram); - P_DEBUG("cached = %8lu KB\n", mem_data->cached); - P_DEBUG("totalswap = %8lu KB\n", mem_data->totalswap); - P_DEBUG("freeswap = %8lu KB\n", mem_data->freeswap); - P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1); - P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2); - P_DEBUG("timestamp = %lX\n", mem_data->timestamp); -} - /* * appldata_get_mem_data() * @@ -140,9 +115,6 @@ static void appldata_get_mem_data(void *data) mem_data->timestamp = get_clock(); mem_data->sync_count_2++; -#ifdef APPLDATA_DEBUG - appldata_debug_print(mem_data); -#endif } @@ -164,17 +136,7 @@ static struct appldata_ops ops = { */ static int __init appldata_mem_init(void) { - int rc; - - P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data)); - - rc = appldata_register_ops(&ops); - if (rc != 0) { - P_ERROR("Error registering ops, rc = %i\n", rc); - } else { - P_DEBUG("%s-ops registered!\n", ops.name); - } - return rc; + return appldata_register_ops(&ops); } /* @@ -185,7 +147,6 @@ static int __init appldata_mem_init(void) static void __exit appldata_mem_exit(void) { appldata_unregister_ops(&ops); - P_DEBUG("%s-ops unregistered!\n", ops.name); } diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 4d834433600..3b746556e1a 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -21,9 +21,6 @@ #include "appldata.h" -#define MY_PRINT_NAME "appldata_net_sum" /* for debug messages, etc. */ - - /* * Network data * @@ -60,26 +57,6 @@ static struct appldata_net_sum_data { } __attribute__((packed)) appldata_net_sum_data; -static inline void appldata_print_debug(struct appldata_net_sum_data *net_data) -{ - P_DEBUG("--- NET - RECORD ---\n"); - - P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces); - P_DEBUG("rx_packets = %8lu\n", net_data->rx_packets); - P_DEBUG("tx_packets = %8lu\n", net_data->tx_packets); - P_DEBUG("rx_bytes = %8lu\n", net_data->rx_bytes); - P_DEBUG("tx_bytes = %8lu\n", net_data->tx_bytes); - P_DEBUG("rx_errors = %8lu\n", net_data->rx_errors); - P_DEBUG("tx_errors = %8lu\n", net_data->tx_errors); - P_DEBUG("rx_dropped = %8lu\n", net_data->rx_dropped); - P_DEBUG("tx_dropped = %8lu\n", net_data->tx_dropped); - P_DEBUG("collisions = %8lu\n", net_data->collisions); - - P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1); - P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2); - P_DEBUG("timestamp = %lX\n", net_data->timestamp); -} - /* * appldata_get_net_sum_data() * @@ -135,9 +112,6 @@ static void appldata_get_net_sum_data(void *data) net_data->timestamp = get_clock(); net_data->sync_count_2++; -#ifdef APPLDATA_DEBUG - appldata_print_debug(net_data); -#endif } @@ -159,17 +133,7 @@ static struct appldata_ops ops = { */ static int __init appldata_net_init(void) { - int rc; - - P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data)); - - rc = appldata_register_ops(&ops); - if (rc != 0) { - P_ERROR("Error registering ops, rc = %i\n", rc); - } else { - P_DEBUG("%s-ops registered!\n", ops.name); - } - return rc; + return appldata_register_ops(&ops); } /* @@ -180,7 +144,6 @@ static int __init appldata_net_init(void) static void __exit appldata_net_exit(void) { appldata_unregister_ops(&ops); - P_DEBUG("%s-ops unregistered!\n", ops.name); } diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c index 6b3eafe1045..eb44f9f8ab9 100644 --- a/arch/s390/appldata/appldata_os.c +++ b/arch/s390/appldata/appldata_os.c @@ -89,44 +89,6 @@ static struct appldata_ops ops = { }; -static inline void appldata_print_debug(struct appldata_os_data *os_data) -{ - int a0, a1, a2, i; - - P_DEBUG("--- OS - RECORD ---\n"); - P_DEBUG("nr_threads = %u\n", os_data->nr_threads); - P_DEBUG("nr_running = %u\n", os_data->nr_running); - P_DEBUG("nr_iowait = %u\n", os_data->nr_iowait); - P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0], - os_data->avenrun[1], os_data->avenrun[2]); - a0 = os_data->avenrun[0]; - a1 = os_data->avenrun[1]; - a2 = os_data->avenrun[2]; - P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n", - LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1), - LOAD_INT(a2), LOAD_FRAC(a2)); - - P_DEBUG("nr_cpus = %u\n", os_data->nr_cpus); - for (i = 0; i < os_data->nr_cpus; i++) { - P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, " - "idle = %u, irq = %u, softirq = %u, iowait = %u, " - "steal = %u\n", - os_data->os_cpu[i].cpu_id, - os_data->os_cpu[i].per_cpu_user, - os_data->os_cpu[i].per_cpu_nice, - os_data->os_cpu[i].per_cpu_system, - os_data->os_cpu[i].per_cpu_idle, - os_data->os_cpu[i].per_cpu_irq, - os_data->os_cpu[i].per_cpu_softirq, - os_data->os_cpu[i].per_cpu_iowait, - os_data->os_cpu[i].per_cpu_steal); - } - - P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1); - P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2); - P_DEBUG("timestamp = %lX\n", os_data->timestamp); -} - /* * appldata_get_os_data() * @@ -180,13 +142,10 @@ static void appldata_get_os_data(void *data) APPLDATA_START_INTERVAL_REC, (unsigned long) ops.data, new_size, ops.mod_lvl); - if (rc != 0) { + if (rc != 0) P_ERROR("os: START NEW DIAG 0xDC failed, " "return code: %d, new size = %i\n", rc, new_size); - P_INFO("os: stopping old record now\n"); - } else - P_INFO("os: new record size = %i\n", new_size); rc = appldata_diag(APPLDATA_RECORD_OS_ID, APPLDATA_STOP_REC, @@ -204,9 +163,6 @@ static void appldata_get_os_data(void *data) } os_data->timestamp = get_clock(); os_data->sync_count_2++; -#ifdef APPLDATA_DEBUG - appldata_print_debug(os_data); -#endif } @@ -227,12 +183,9 @@ static int __init appldata_os_init(void) rc = -ENOMEM; goto out; } - P_DEBUG("max. sizeof(os) = %i, sizeof(os_cpu) = %lu\n", max_size, - sizeof(struct appldata_os_per_cpu)); appldata_os_data = kzalloc(max_size, GFP_DMA); if (appldata_os_data == NULL) { - P_ERROR("No memory for %s!\n", ops.name); rc = -ENOMEM; goto out; } @@ -240,17 +193,12 @@ static int __init appldata_os_init(void) appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu); appldata_os_data->cpu_offset = offsetof(struct appldata_os_data, os_cpu); - P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset); ops.data = appldata_os_data; ops.callback = &appldata_get_os_data; rc = appldata_register_ops(&ops); - if (rc != 0) { - P_ERROR("Error registering ops, rc = %i\n", rc); + if (rc != 0) kfree(appldata_os_data); - } else { - P_DEBUG("%s-ops registered!\n", ops.name); - } out: return rc; } @@ -264,7 +212,6 @@ static void __exit appldata_os_exit(void) { appldata_unregister_ops(&ops); kfree(appldata_os_data); - P_DEBUG("%s-ops unregistered!\n", ops.name); } diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c index 0cfefddd837..6a4300b3ff5 100644 --- a/arch/s390/crypto/prng.c +++ b/arch/s390/crypto/prng.c @@ -185,11 +185,8 @@ static int __init prng_init(void) prng_seed(16); ret = misc_register(&prng_dev); - if (ret) { - printk(KERN_WARNING - "Could not register misc device for PRNG.\n"); + if (ret) goto out_buf; - } return 0; out_buf: diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 4b010ff814c..7383781f3e6 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -150,33 +150,24 @@ static ssize_t hypfs_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t offset) { char *data; - size_t len; + ssize_t ret; struct file *filp = iocb->ki_filp; /* XXX: temporary */ char __user *buf = iov[0].iov_base; size_t count = iov[0].iov_len; - if (nr_segs != 1) { - count = -EINVAL; - goto out; - } + if (nr_segs != 1) + return -EINVAL; data = filp->private_data; - len = strlen(data); - if (offset > len) { - count = 0; - goto out; - } - if (count > len - offset) - count = len - offset; - if (copy_to_user(buf, data + offset, count)) { - count = -EFAULT; - goto out; - } - iocb->ki_pos += count; + ret = simple_read_from_buffer(buf, count, &offset, data, strlen(data)); + if (ret <= 0) + return ret; + + iocb->ki_pos += ret; file_accessed(filp); -out: - return count; + + return ret; } static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t offset) diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 6302f508258..50f657e7734 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -7,9 +7,14 @@ # CFLAGS_smp.o := -Wno-nonnull +# +# Pass UTS_MACHINE for user_regset definition +# +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + obj-y := bitmap.o traps.o time.o process.o base.o early.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - s390_ext.o debug.o irq.o ipl.o dis.o diag.o + s390_ext.o debug.o irq.o ipl.o dis.o diag.o mem_detect.o obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o) obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o) @@ -23,7 +28,7 @@ obj-$(CONFIG_AUDIT) += audit.o compat-obj-$(CONFIG_AUDIT) += compat_audit.o obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \ compat_wrapper.o compat_exec_domain.o \ - binfmt_elf32.o $(compat-obj-y) + $(compat-obj-y) obj-$(CONFIG_VIRT_TIMER) += vtime.o obj-$(CONFIG_STACKTRACE) += stacktrace.o diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c deleted file mode 100644 index 3e1c315b736..00000000000 --- a/arch/s390/kernel/binfmt_elf32.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Support for 32-bit Linux for S390 ELF binaries. - * - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Gerhard Tonn (ton@de.ibm.com) - * - * Heavily inspired by the 32-bit Sparc compat code which is - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - */ - -#define __ASMS390_ELF_H - -#include <linux/time.h> - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_S390 - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) \ - (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \ - && (x)->e_ident[EI_CLASS] == ELF_CLASS) - -/* ELF register definitions */ -#define NUM_GPRS 16 -#define NUM_FPRS 16 -#define NUM_ACRS 16 - -/* For SVR4/S390 the function pointer to be registered with `atexit` is - passed in R14. */ -#define ELF_PLAT_INIT(_r, load_addr) \ - do { \ - _r->gprs[14] = 0; \ - } while(0) - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) - -/* Wow, the "main" arch needs arch dependent functions too.. :) */ - -/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is - now struct_user_regs, they are different) */ - -#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs32(regs, &pr_reg); - -#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs) - -#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs) - -/* This yields a mask that user programs can use to figure out what - instruction set this CPU supports. */ - -#define ELF_HWCAP (0) - -/* This yields a string that ld.so will use to load implementation - specific libraries for optimization. This is more specific in - intent than poking at uname or /proc/cpuinfo. - - For the moment, we have only optimizations for the Intel generations, - but that could change... */ - -#define ELF_PLATFORM (NULL) - -#define SET_PERSONALITY(ex, ibcs2) \ -do { \ - if (ibcs2) \ - set_personality(PER_SVR4); \ - else if (current->personality != PER_LINUX32) \ - set_personality(PER_LINUX); \ - set_thread_flag(TIF_31BIT); \ -} while (0) - -#include "compat_linux.h" - -typedef _s390_fp_regs32 elf_fpregset_t; - -typedef struct -{ - - _psw_t32 psw; - __u32 gprs[__NUM_GPRS]; - __u32 acrs[__NUM_ACRS]; - __u32 orig_gpr2; -} s390_regs32; -typedef s390_regs32 elf_gregset_t; - -static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs) -{ - int i; - - memcpy(®s->psw.mask, &ptregs->psw.mask, 4); - memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); - for (i = 0; i < NUM_GPRS; i++) - regs->gprs[i] = ptregs->gprs[i]; - save_access_regs(regs->acrs); - regs->orig_gpr2 = ptregs->orig_gpr2; - return 1; -} - -static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs) -{ - struct pt_regs *ptregs = task_pt_regs(tsk); - int i; - - memcpy(®s->psw.mask, &ptregs->psw.mask, 4); - memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); - for (i = 0; i < NUM_GPRS; i++) - regs->gprs[i] = ptregs->gprs[i]; - memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs)); - regs->orig_gpr2 = ptregs->orig_gpr2; - return 1; -} - -static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) -{ - if (tsk == current) - save_fp_regs((s390_fp_regs *) fpregs); - else - memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t)); - return 1; -} - -#include <asm/processor.h> -#include <asm/pgalloc.h> -#include <linux/module.h> -#include <linux/elfcore.h> -#include <linux/binfmts.h> -#include <linux/compat.h> - -#define elf_prstatus elf_prstatus32 -struct elf_prstatus32 -{ - struct elf_siginfo pr_info; /* Info associated with signal */ - short pr_cursig; /* Current signal */ - u32 pr_sigpend; /* Set of pending signals */ - u32 pr_sighold; /* Set of held signals */ - pid_t pr_pid; - pid_t pr_ppid; - pid_t pr_pgrp; - pid_t pr_sid; - struct compat_timeval pr_utime; /* User time */ - struct compat_timeval pr_stime; /* System time */ - struct compat_timeval pr_cutime; /* Cumulative user time */ - struct compat_timeval pr_cstime; /* Cumulative system time */ - elf_gregset_t pr_reg; /* GP registers */ - int pr_fpvalid; /* True if math co-processor being used. */ -}; - -#define elf_prpsinfo elf_prpsinfo32 -struct elf_prpsinfo32 -{ - char pr_state; /* numeric process state */ - char pr_sname; /* char for pr_state */ - char pr_zomb; /* zombie */ - char pr_nice; /* nice val */ - u32 pr_flag; /* flags */ - u16 pr_uid; - u16 pr_gid; - pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; - /* Lots missing */ - char pr_fname[16]; /* filename of executable */ - char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ -}; - -#include <linux/highuid.h> - -/* -#define init_elf_binfmt init_elf32_binfmt -*/ - -#undef start_thread -#define start_thread start_thread31 - -static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw, - unsigned long new_stackp) -{ - set_fs(USER_DS); - regs->psw.mask = psw_user32_bits; - regs->psw.addr = new_psw; - regs->gprs[15] = new_stackp; - crst_table_downgrade(current->mm, 1UL << 31); -} - -MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," - " Copyright 2000 IBM Corporation"); -MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>"); - -#undef MODULE_DESCRIPTION -#undef MODULE_AUTHOR - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static inline void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - value->tv_usec = cputime % 1000000; - value->tv_sec = cputime / 1000000; -} - -#include "../../../fs/binfmt_elf.c" - diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h index 419aef913ee..cde81fa64f8 100644 --- a/arch/s390/kernel/compat_ptrace.h +++ b/arch/s390/kernel/compat_ptrace.h @@ -1,7 +1,7 @@ #ifndef _PTRACE32_H #define _PTRACE32_H -#include "compat_linux.h" /* needed for _psw_t32 */ +#include "compat_linux.h" /* needed for psw_compat_t */ typedef struct { __u32 cr[3]; @@ -38,7 +38,7 @@ typedef struct { struct user_regs_struct32 { - _psw_t32 psw; + psw_compat_t psw; u32 gprs[NUM_GPRS]; u32 acrs[NUM_ACRS]; u32 orig_gpr2; diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index c93d1296cc0..d80fcd4a7fe 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -1079,7 +1079,6 @@ __init debug_init(void) s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table); mutex_lock(&debug_mutex); debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL); - printk(KERN_INFO "debug: Initialization complete\n"); initialized = 1; mutex_unlock(&debug_mutex); @@ -1193,7 +1192,6 @@ debug_get_uint(char *buf) for(; isspace(*buf); buf++); rc = simple_strtoul(buf, &buf, 10); if(*buf){ - printk("debug: no integer specified!\n"); rc = -EINVAL; } return rc; @@ -1340,19 +1338,12 @@ static void debug_flush(debug_info_t* id, int area) memset(id->areas[i][j], 0, PAGE_SIZE); } } - printk(KERN_INFO "debug: %s: all areas flushed\n",id->name); } else if(area >= 0 && area < id->nr_areas) { id->active_entries[area] = 0; id->active_pages[area] = 0; for(i = 0; i < id->pages_per_area; i++) { memset(id->areas[area][i],0,PAGE_SIZE); } - printk(KERN_INFO "debug: %s: area %i has been flushed\n", - id->name, area); - } else { - printk(KERN_INFO - "debug: %s: area %i cannot be flushed (range: %i - %i)\n", - id->name, area, 0, id->nr_areas-1); } spin_unlock_irqrestore(&id->lock,flags); } diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index d0e09684b9c..2a2ca268b1d 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/pfn.h> #include <linux/uaccess.h> +#include <asm/ebcdic.h> #include <asm/ipl.h> #include <asm/lowcore.h> #include <asm/processor.h> @@ -26,12 +27,40 @@ /* * Create a Kernel NSS if the SAVESYS= parameter is defined */ -#define DEFSYS_CMD_SIZE 96 +#define DEFSYS_CMD_SIZE 128 #define SAVESYS_CMD_SIZE 32 char kernel_nss_name[NSS_NAME_SIZE + 1]; +static void __init setup_boot_command_line(void); + + #ifdef CONFIG_SHARED_KERNEL +int __init savesys_ipl_nss(char *cmd, const int cmdlen); + +asm( + " .section .init.text,\"ax\",@progbits\n" + " .align 4\n" + " .type savesys_ipl_nss, @function\n" + "savesys_ipl_nss:\n" +#ifdef CONFIG_64BIT + " stmg 6,15,48(15)\n" + " lgr 14,3\n" + " sam31\n" + " diag 2,14,0x8\n" + " sam64\n" + " lgr 2,14\n" + " lmg 6,15,48(15)\n" +#else + " stm 6,15,24(15)\n" + " lr 14,3\n" + " diag 2,14,0x8\n" + " lr 2,14\n" + " lm 6,15,24(15)\n" +#endif + " br 14\n" + " .size savesys_ipl_nss, .-savesys_ipl_nss\n"); + static noinline __init void create_kernel_nss(void) { unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size; @@ -39,6 +68,7 @@ static noinline __init void create_kernel_nss(void) unsigned int sinitrd_pfn, einitrd_pfn; #endif int response; + size_t len; char *savesys_ptr; char upper_command_line[COMMAND_LINE_SIZE]; char defsys_cmd[DEFSYS_CMD_SIZE]; @@ -49,8 +79,8 @@ static noinline __init void create_kernel_nss(void) return; /* Convert COMMAND_LINE to upper case */ - for (i = 0; i < strlen(COMMAND_LINE); i++) - upper_command_line[i] = toupper(COMMAND_LINE[i]); + for (i = 0; i < strlen(boot_command_line); i++) + upper_command_line[i] = toupper(boot_command_line[i]); savesys_ptr = strstr(upper_command_line, "SAVESYS="); @@ -83,7 +113,8 @@ static noinline __init void create_kernel_nss(void) } #endif - sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK", defsys_cmd, min_size); + sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13", + defsys_cmd, min_size); sprintf(savesys_cmd, "SAVESYS %s \n IPL %s", kernel_nss_name, kernel_nss_name); @@ -94,13 +125,24 @@ static noinline __init void create_kernel_nss(void) return; } - __cpcmd(savesys_cmd, NULL, 0, &response); + len = strlen(savesys_cmd); + ASCEBC(savesys_cmd, len); + response = savesys_ipl_nss(savesys_cmd, len); - if (response != strlen(savesys_cmd)) { + /* On success: response is equal to the command size, + * max SAVESYS_CMD_SIZE + * On error: response contains the numeric portion of cp error message. + * for SAVESYS it will be >= 263 + */ + if (response > SAVESYS_CMD_SIZE) { kernel_nss_name[0] = '\0'; return; } + /* re-setup boot command line with new ipl vm parms */ + ipl_update_parameters(); + setup_boot_command_line(); + ipl_flags = IPL_NSS_VALID; } @@ -141,109 +183,11 @@ static noinline __init void detect_machine_type(void) if (cpuinfo->cpu_id.version == 0xff) machine_flags |= MACHINE_FLAG_VM; - /* Running on a P/390 ? */ - if (cpuinfo->cpu_id.machine == 0x7490) - machine_flags |= MACHINE_FLAG_P390; - /* Running under KVM ? */ if (cpuinfo->cpu_id.version == 0xfe) machine_flags |= MACHINE_FLAG_KVM; } -#ifdef CONFIG_64BIT -static noinline __init int memory_fast_detect(void) -{ - unsigned long val0 = 0; - unsigned long val1 = 0xc; - int ret = -ENOSYS; - - if (ipl_flags & IPL_NSS_VALID) - return -ENOSYS; - - asm volatile( - " diag %1,%2,0x260\n" - "0: lhi %0,0\n" - "1:\n" - EX_TABLE(0b,1b) - : "+d" (ret), "+d" (val0), "+d" (val1) : : "cc"); - - if (ret || val0 != val1) - return -ENOSYS; - - memory_chunk[0].size = val0 + 1; - return 0; -} -#else -static inline int memory_fast_detect(void) -{ - return -ENOSYS; -} -#endif - -static inline __init unsigned long __tprot(unsigned long addr) -{ - int cc = -1; - - asm volatile( - " tprot 0(%1),0\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - EX_TABLE(0b,1b) - : "+d" (cc) : "a" (addr) : "cc"); - return (unsigned long)cc; -} - -/* Checking memory in 128KB increments. */ -#define CHUNK_INCR (1UL << 17) -#define ADDR2G (1UL << 31) - -static noinline __init void find_memory_chunks(unsigned long memsize) -{ - unsigned long addr = 0, old_addr = 0; - unsigned long old_cc = CHUNK_READ_WRITE; - unsigned long cc; - int chunk = 0; - - while (chunk < MEMORY_CHUNKS) { - cc = __tprot(addr); - while (cc == old_cc) { - addr += CHUNK_INCR; - if (memsize && addr >= memsize) - break; -#ifndef CONFIG_64BIT - if (addr == ADDR2G) - break; -#endif - cc = __tprot(addr); - } - - if (old_addr != addr && - (old_cc == CHUNK_READ_WRITE || old_cc == CHUNK_READ_ONLY)) { - memory_chunk[chunk].addr = old_addr; - memory_chunk[chunk].size = addr - old_addr; - memory_chunk[chunk].type = old_cc; - chunk++; - } - - old_addr = addr; - old_cc = cc; - -#ifndef CONFIG_64BIT - if (addr == ADDR2G) - break; -#endif - /* - * Finish memory detection at the first hole - * if storage size is unknown. - */ - if (cc == -1UL && !memsize) - break; - if (memsize && addr >= memsize) - break; - } -} - static __init void early_pgm_check_handler(void) { unsigned long addr; @@ -380,23 +324,61 @@ static __init void detect_machine_facilities(void) #endif } +static __init void rescue_initrd(void) +{ +#ifdef CONFIG_BLK_DEV_INITRD + /* + * Move the initrd right behind the bss section in case it starts + * within the bss section. So we don't overwrite it when the bss + * section gets cleared. + */ + if (!INITRD_START || !INITRD_SIZE) + return; + if (INITRD_START >= (unsigned long) __bss_stop) + return; + memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE); + INITRD_START = (unsigned long) __bss_stop; +#endif +} + +/* Set up boot command line */ +static void __init setup_boot_command_line(void) +{ + char *parm = NULL; + + /* copy arch command line */ + strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE); + boot_command_line[ARCH_COMMAND_LINE_SIZE - 1] = 0; + + /* append IPL PARM data to the boot command line */ + if (MACHINE_IS_VM) { + parm = boot_command_line + strlen(boot_command_line); + *parm++ = ' '; + get_ipl_vmparm(parm); + if (parm[0] == '=') + memmove(boot_command_line, parm + 1, strlen(parm)); + } +} + + /* * Save ipl parameters, clear bss memory, initialize storage keys * and create a kernel NSS at startup if the SAVESYS= parm is defined */ void __init startup_init(void) { - unsigned long long memsize; - ipl_save_parameters(); + rescue_initrd(); clear_bss_section(); init_kernel_storage_key(); lockdep_init(); lockdep_off(); - detect_machine_type(); - create_kernel_nss(); sort_main_extable(); setup_lowcore_early(); + detect_machine_type(); + ipl_update_parameters(); + setup_boot_command_line(); + create_kernel_nss(); detect_mvpg(); detect_ieee(); detect_csp(); @@ -404,18 +386,7 @@ void __init startup_init(void) detect_diag44(); detect_machine_facilities(); setup_hpage(); - sclp_read_info_early(); sclp_facilities_detect(); - memsize = sclp_memory_detect(); -#ifndef CONFIG_64BIT - /* - * Can't deal with more than 2G in 31 bit addressing mode, so - * limit the value in order to avoid strange side effects. - */ - if (memsize > ADDR2G) - memsize = ADDR2G; -#endif - if (memory_fast_detect() < 0) - find_memory_chunks((unsigned long) memsize); + detect_memory_layout(memory_chunk); lockdep_on(); } diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 532542447d6..54b2779b5e2 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -14,6 +14,7 @@ #include <linux/delay.h> #include <linux/reboot.h> #include <linux/ctype.h> +#include <linux/fs.h> #include <asm/ipl.h> #include <asm/smp.h> #include <asm/setup.h> @@ -22,6 +23,7 @@ #include <asm/ebcdic.h> #include <asm/reset.h> #include <asm/sclp.h> +#include <asm/setup.h> #define IPL_PARM_BLOCK_VERSION 0 @@ -121,6 +123,7 @@ enum ipl_method { REIPL_METHOD_FCP_RO_VM, REIPL_METHOD_FCP_DUMP, REIPL_METHOD_NSS, + REIPL_METHOD_NSS_DIAG, REIPL_METHOD_DEFAULT, }; @@ -134,14 +137,15 @@ enum dump_method { static int diag308_set_works = 0; +static struct ipl_parameter_block ipl_block; + static int reipl_capabilities = IPL_TYPE_UNKNOWN; static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT; static struct ipl_parameter_block *reipl_block_fcp; static struct ipl_parameter_block *reipl_block_ccw; - -static char reipl_nss_name[NSS_NAME_SIZE + 1]; +static struct ipl_parameter_block *reipl_block_nss; static int dump_capabilities = DUMP_TYPE_NONE; static enum dump_type dump_type = DUMP_TYPE_NONE; @@ -263,6 +267,56 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); +/* VM IPL PARM routines */ +static void reipl_get_ascii_vmparm(char *dest, + const struct ipl_parameter_block *ipb) +{ + int i; + int len = 0; + char has_lowercase = 0; + + if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) && + (ipb->ipl_info.ccw.vm_parm_len > 0)) { + + len = ipb->ipl_info.ccw.vm_parm_len; + memcpy(dest, ipb->ipl_info.ccw.vm_parm, len); + /* If at least one character is lowercase, we assume mixed + * case; otherwise we convert everything to lowercase. + */ + for (i = 0; i < len; i++) + if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */ + (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */ + (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */ + has_lowercase = 1; + break; + } + if (!has_lowercase) + EBC_TOLOWER(dest, len); + EBCASC(dest, len); + } + dest[len] = 0; +} + +void get_ipl_vmparm(char *dest) +{ + if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)) + reipl_get_ascii_vmparm(dest, &ipl_block); + else + dest[0] = 0; +} + +static ssize_t ipl_vm_parm_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + char parm[DIAG308_VMPARM_SIZE + 1] = {}; + + get_ipl_vmparm(parm); + return sprintf(page, "%s\n", parm); +} + +static struct kobj_attribute sys_ipl_vm_parm_attr = + __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); + static ssize_t sys_ipl_device_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { @@ -285,14 +339,8 @@ static struct kobj_attribute sys_ipl_device_attr = static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { - unsigned int size = IPL_PARMBLOCK_SIZE; - - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count); - return count; + return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START, + IPL_PARMBLOCK_SIZE); } static struct bin_attribute ipl_parameter_attr = { @@ -310,12 +358,7 @@ static ssize_t ipl_scp_data_read(struct kobject *kobj, struct bin_attribute *att unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, scp_data + off, count); - return count; + return memory_read_from_buffer(buf, count, &off, scp_data, size); } static struct bin_attribute ipl_scp_data_attr = { @@ -370,15 +413,27 @@ static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, static struct kobj_attribute sys_ipl_ccw_loadparm_attr = __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); -static struct attribute *ipl_ccw_attrs[] = { +static struct attribute *ipl_ccw_attrs_vm[] = { &sys_ipl_type_attr.attr, &sys_ipl_device_attr.attr, &sys_ipl_ccw_loadparm_attr.attr, + &sys_ipl_vm_parm_attr.attr, NULL, }; -static struct attribute_group ipl_ccw_attr_group = { - .attrs = ipl_ccw_attrs, +static struct attribute *ipl_ccw_attrs_lpar[] = { + &sys_ipl_type_attr.attr, + &sys_ipl_device_attr.attr, + &sys_ipl_ccw_loadparm_attr.attr, + NULL, +}; + +static struct attribute_group ipl_ccw_attr_group_vm = { + .attrs = ipl_ccw_attrs_vm, +}; + +static struct attribute_group ipl_ccw_attr_group_lpar = { + .attrs = ipl_ccw_attrs_lpar }; /* NSS ipl device attributes */ @@ -388,6 +443,8 @@ DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name); static struct attribute *ipl_nss_attrs[] = { &sys_ipl_type_attr.attr, &sys_ipl_nss_name_attr.attr, + &sys_ipl_ccw_loadparm_attr.attr, + &sys_ipl_vm_parm_attr.attr, NULL, }; @@ -450,7 +507,12 @@ static int __init ipl_init(void) } switch (ipl_info.type) { case IPL_TYPE_CCW: - rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group); + if (MACHINE_IS_VM) + rc = sysfs_create_group(&ipl_kset->kobj, + &ipl_ccw_attr_group_vm); + else + rc = sysfs_create_group(&ipl_kset->kobj, + &ipl_ccw_attr_group_lpar); break; case IPL_TYPE_FCP: case IPL_TYPE_FCP_DUMP: @@ -481,6 +543,83 @@ static struct shutdown_action __refdata ipl_action = { * reipl shutdown action: Reboot Linux on shutdown. */ +/* VM IPL PARM attributes */ +static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, + char *page) +{ + char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; + + reipl_get_ascii_vmparm(vmparm, ipb); + return sprintf(page, "%s\n", vmparm); +} + +static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb, + size_t vmparm_max, + const char *buf, size_t len) +{ + int i, ip_len; + + /* ignore trailing newline */ + ip_len = len; + if ((len > 0) && (buf[len - 1] == '\n')) + ip_len--; + + if (ip_len > vmparm_max) + return -EINVAL; + + /* parm is used to store kernel options, check for common chars */ + for (i = 0; i < ip_len; i++) + if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i]))) + return -EINVAL; + + memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE); + ipb->ipl_info.ccw.vm_parm_len = ip_len; + if (ip_len > 0) { + ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; + memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len); + ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len); + } else { + ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID; + } + + return len; +} + +/* NSS wrapper */ +static ssize_t reipl_nss_vmparm_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return reipl_generic_vmparm_show(reipl_block_nss, page); +} + +static ssize_t reipl_nss_vmparm_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len); +} + +/* CCW wrapper */ +static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return reipl_generic_vmparm_show(reipl_block_ccw, page); +} + +static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len); +} + +static struct kobj_attribute sys_reipl_nss_vmparm_attr = + __ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show, + reipl_nss_vmparm_store); +static struct kobj_attribute sys_reipl_ccw_vmparm_attr = + __ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show, + reipl_ccw_vmparm_store); + /* FCP reipl device attributes */ DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n", @@ -513,27 +652,26 @@ static struct attribute_group reipl_fcp_attr_group = { DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", reipl_block_ccw->ipl_info.ccw.devno); -static void reipl_get_ascii_loadparm(char *loadparm) +static void reipl_get_ascii_loadparm(char *loadparm, + struct ipl_parameter_block *ibp) { - memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param, - LOADPARM_LEN); + memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN); EBCASC(loadparm, LOADPARM_LEN); loadparm[LOADPARM_LEN] = 0; strstrip(loadparm); } -static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, - struct kobj_attribute *attr, char *page) +static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb, + char *page) { char buf[LOADPARM_LEN + 1]; - reipl_get_ascii_loadparm(buf); + reipl_get_ascii_loadparm(buf, ipb); return sprintf(page, "%s\n", buf); } -static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t len) +static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb, + const char *buf, size_t len) { int i, lp_len; @@ -552,35 +690,128 @@ static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, return -EINVAL; } /* initialize loadparm with blanks */ - memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN); + memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN); /* copy and convert to ebcdic */ - memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len); - ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN); + memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len); + ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN); return len; } +/* NSS wrapper */ +static ssize_t reipl_nss_loadparm_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return reipl_generic_loadparm_show(reipl_block_nss, page); +} + +static ssize_t reipl_nss_loadparm_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return reipl_generic_loadparm_store(reipl_block_nss, buf, len); +} + +/* CCW wrapper */ +static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return reipl_generic_loadparm_show(reipl_block_ccw, page); +} + +static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + return reipl_generic_loadparm_store(reipl_block_ccw, buf, len); +} + static struct kobj_attribute sys_reipl_ccw_loadparm_attr = - __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, - reipl_ccw_loadparm_store); + __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show, + reipl_ccw_loadparm_store); -static struct attribute *reipl_ccw_attrs[] = { +static struct attribute *reipl_ccw_attrs_vm[] = { &sys_reipl_ccw_device_attr.attr, &sys_reipl_ccw_loadparm_attr.attr, + &sys_reipl_ccw_vmparm_attr.attr, NULL, }; -static struct attribute_group reipl_ccw_attr_group = { +static struct attribute *reipl_ccw_attrs_lpar[] = { + &sys_reipl_ccw_device_attr.attr, + &sys_reipl_ccw_loadparm_attr.attr, + NULL, +}; + +static struct attribute_group reipl_ccw_attr_group_vm = { + .name = IPL_CCW_STR, + .attrs = reipl_ccw_attrs_vm, +}; + +static struct attribute_group reipl_ccw_attr_group_lpar = { .name = IPL_CCW_STR, - .attrs = reipl_ccw_attrs, + .attrs = reipl_ccw_attrs_lpar, }; /* NSS reipl device attributes */ +static void reipl_get_ascii_nss_name(char *dst, + struct ipl_parameter_block *ipb) +{ + memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE); + EBCASC(dst, NSS_NAME_SIZE); + dst[NSS_NAME_SIZE] = 0; +} + +static ssize_t reipl_nss_name_show(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + char nss_name[NSS_NAME_SIZE + 1] = {}; -DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name); + reipl_get_ascii_nss_name(nss_name, reipl_block_nss); + return sprintf(page, "%s\n", nss_name); +} + +static ssize_t reipl_nss_name_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int nss_len; + + /* ignore trailing newline */ + nss_len = len; + if ((len > 0) && (buf[len - 1] == '\n')) + nss_len--; + + if (nss_len > NSS_NAME_SIZE) + return -EINVAL; + + memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE); + if (nss_len > 0) { + reipl_block_nss->ipl_info.ccw.vm_flags |= + DIAG308_VM_FLAGS_NSS_VALID; + memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len); + ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); + EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len); + } else { + reipl_block_nss->ipl_info.ccw.vm_flags &= + ~DIAG308_VM_FLAGS_NSS_VALID; + } + + return len; +} + +static struct kobj_attribute sys_reipl_nss_name_attr = + __ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show, + reipl_nss_name_store); + +static struct kobj_attribute sys_reipl_nss_loadparm_attr = + __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show, + reipl_nss_loadparm_store); static struct attribute *reipl_nss_attrs[] = { &sys_reipl_nss_name_attr.attr, + &sys_reipl_nss_loadparm_attr.attr, + &sys_reipl_nss_vmparm_attr.attr, NULL, }; @@ -617,7 +848,10 @@ static int reipl_set_type(enum ipl_type type) reipl_method = REIPL_METHOD_FCP_DUMP; break; case IPL_TYPE_NSS: - reipl_method = REIPL_METHOD_NSS; + if (diag308_set_works) + reipl_method = REIPL_METHOD_NSS_DIAG; + else + reipl_method = REIPL_METHOD_NSS; break; case IPL_TYPE_UNKNOWN: reipl_method = REIPL_METHOD_DEFAULT; @@ -655,11 +889,38 @@ static struct kobj_attribute reipl_type_attr = static struct kset *reipl_kset; +static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb, + const enum ipl_method m) +{ + char loadparm[LOADPARM_LEN + 1] = {}; + char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; + char nss_name[NSS_NAME_SIZE + 1] = {}; + size_t pos = 0; + + reipl_get_ascii_loadparm(loadparm, ipb); + reipl_get_ascii_nss_name(nss_name, ipb); + reipl_get_ascii_vmparm(vmparm, ipb); + + switch (m) { + case REIPL_METHOD_CCW_VM: + pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno); + break; + case REIPL_METHOD_NSS: + pos = sprintf(dst, "IPL %s", nss_name); + break; + default: + break; + } + if (strlen(loadparm) > 0) + pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm); + if (strlen(vmparm) > 0) + sprintf(dst + pos, " PARM %s", vmparm); +} + static void reipl_run(struct shutdown_trigger *trigger) { struct ccw_dev_id devid; - static char buf[100]; - char loadparm[LOADPARM_LEN + 1]; + static char buf[128]; switch (reipl_method) { case REIPL_METHOD_CCW_CIO: @@ -668,13 +929,7 @@ static void reipl_run(struct shutdown_trigger *trigger) reipl_ccw_dev(&devid); break; case REIPL_METHOD_CCW_VM: - reipl_get_ascii_loadparm(loadparm); - if (strlen(loadparm) == 0) - sprintf(buf, "IPL %X CLEAR", - reipl_block_ccw->ipl_info.ccw.devno); - else - sprintf(buf, "IPL %X CLEAR LOADPARM '%s'", - reipl_block_ccw->ipl_info.ccw.devno, loadparm); + get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM); __cpcmd(buf, NULL, 0, NULL); break; case REIPL_METHOD_CCW_DIAG: @@ -691,8 +946,12 @@ static void reipl_run(struct shutdown_trigger *trigger) case REIPL_METHOD_FCP_RO_VM: __cpcmd("IPL", NULL, 0, NULL); break; + case REIPL_METHOD_NSS_DIAG: + diag308(DIAG308_SET, reipl_block_nss); + diag308(DIAG308_IPL, NULL); + break; case REIPL_METHOD_NSS: - sprintf(buf, "IPL %s", reipl_nss_name); + get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS); __cpcmd(buf, NULL, 0, NULL); break; case REIPL_METHOD_DEFAULT: @@ -707,16 +966,36 @@ static void reipl_run(struct shutdown_trigger *trigger) disabled_wait((unsigned long) __builtin_return_address(0)); } -static void __init reipl_probe(void) +static void reipl_block_ccw_init(struct ipl_parameter_block *ipb) { - void *buffer; + ipb->hdr.len = IPL_PARM_BLK_CCW_LEN; + ipb->hdr.version = IPL_PARM_BLOCK_VERSION; + ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; + ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW; +} - buffer = (void *) get_zeroed_page(GFP_KERNEL); - if (!buffer) - return; - if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) - diag308_set_works = 1; - free_page((unsigned long)buffer); +static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb) +{ + /* LOADPARM */ + /* check if read scp info worked and set loadparm */ + if (sclp_ipl_info.is_valid) + memcpy(ipb->ipl_info.ccw.load_parm, + &sclp_ipl_info.loadparm, LOADPARM_LEN); + else + /* read scp info failed: set empty loadparm (EBCDIC blanks) */ + memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN); + ipb->hdr.flags = DIAG308_FLAGS_LP_VALID; + + /* VM PARM */ + if (MACHINE_IS_VM && diag308_set_works && + (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) { + + ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID; + ipb->ipl_info.ccw.vm_parm_len = + ipl_block.ipl_info.ccw.vm_parm_len; + memcpy(ipb->ipl_info.ccw.vm_parm, + ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE); + } } static int __init reipl_nss_init(void) @@ -725,10 +1004,31 @@ static int __init reipl_nss_init(void) if (!MACHINE_IS_VM) return 0; + + reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL); + if (!reipl_block_nss) + return -ENOMEM; + + if (!diag308_set_works) + sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO; + rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); if (rc) return rc; - strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); + + reipl_block_ccw_init(reipl_block_nss); + if (ipl_info.type == IPL_TYPE_NSS) { + memset(reipl_block_nss->ipl_info.ccw.nss_name, + ' ', NSS_NAME_SIZE); + memcpy(reipl_block_nss->ipl_info.ccw.nss_name, + kernel_nss_name, strlen(kernel_nss_name)); + ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE); + reipl_block_nss->ipl_info.ccw.vm_flags |= + DIAG308_VM_FLAGS_NSS_VALID; + + reipl_block_ccw_fill_parms(reipl_block_nss); + } + reipl_capabilities |= IPL_TYPE_NSS; return 0; } @@ -740,28 +1040,27 @@ static int __init reipl_ccw_init(void) reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); if (!reipl_block_ccw) return -ENOMEM; - rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group); - if (rc) { - free_page((unsigned long)reipl_block_ccw); - return rc; + + if (MACHINE_IS_VM) { + if (!diag308_set_works) + sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO; + rc = sysfs_create_group(&reipl_kset->kobj, + &reipl_ccw_attr_group_vm); + } else { + if(!diag308_set_works) + sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; + rc = sysfs_create_group(&reipl_kset->kobj, + &reipl_ccw_attr_group_lpar); } - reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN; - reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; - reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; - reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; - reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID; - /* check if read scp info worked and set loadparm */ - if (sclp_ipl_info.is_valid) - memcpy(reipl_block_ccw->ipl_info.ccw.load_param, - &sclp_ipl_info.loadparm, LOADPARM_LEN); - else - /* read scp info failed: set empty loadparm (EBCDIC blanks) */ - memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, - LOADPARM_LEN); - if (!MACHINE_IS_VM && !diag308_set_works) - sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; - if (ipl_info.type == IPL_TYPE_CCW) + if (rc) + return rc; + + reipl_block_ccw_init(reipl_block_ccw); + if (ipl_info.type == IPL_TYPE_CCW) { reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; + reipl_block_ccw_fill_parms(reipl_block_ccw); + } + reipl_capabilities |= IPL_TYPE_CCW; return 0; } @@ -1298,7 +1597,6 @@ static void __init shutdown_actions_init(void) static int __init s390_ipl_init(void) { - reipl_probe(); sclp_get_ipl_info(&sclp_ipl_info); shutdown_actions_init(); shutdown_triggers_init(); @@ -1405,6 +1703,12 @@ void __init setup_ipl(void) atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); } +void __init ipl_update_parameters(void) +{ + if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK) + diag308_set_works = 1; +} + void __init ipl_save_parameters(void) { struct cio_iplinfo iplinfo; diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index ed04d1372d5..288ad490a6d 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -41,10 +41,8 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) if (is_prohibited_opcode((kprobe_opcode_t *) p->addr)) return -EINVAL; - if ((unsigned long)p->addr & 0x01) { - printk("Attempt to register kprobe at an unaligned address\n"); + if ((unsigned long)p->addr & 0x01) return -EINVAL; - } /* Use the get_insn_slot() facility for correctness */ if (!(p->ainsn.insn = get_insn_slot())) diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 3c77dd36994..131d7ee8b41 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -52,7 +52,6 @@ void machine_kexec_cleanup(struct kimage *image) void machine_shutdown(void) { - printk(KERN_INFO "kexec: machine_shutdown called\n"); } void machine_kexec(struct kimage *image) diff --git a/arch/s390/kernel/mem_detect.c b/arch/s390/kernel/mem_detect.c new file mode 100644 index 00000000000..18ed7abe16c --- /dev/null +++ b/arch/s390/kernel/mem_detect.c @@ -0,0 +1,100 @@ +/* + * Copyright IBM Corp. 2008 + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/ipl.h> +#include <asm/sclp.h> +#include <asm/setup.h> + +static int memory_fast_detect(struct mem_chunk *chunk) +{ + unsigned long val0 = 0; + unsigned long val1 = 0xc; + int rc = -EOPNOTSUPP; + + if (ipl_flags & IPL_NSS_VALID) + return -EOPNOTSUPP; + asm volatile( + " diag %1,%2,0x260\n" + "0: lhi %0,0\n" + "1:\n" + EX_TABLE(0b,1b) + : "+d" (rc), "+d" (val0), "+d" (val1) : : "cc"); + + if (rc || val0 != val1) + return -EOPNOTSUPP; + chunk->size = val0 + 1; + return 0; +} + +static inline int tprot(unsigned long addr) +{ + int rc = -EFAULT; + + asm volatile( + " tprot 0(%1),0\n" + "0: ipm %0\n" + " srl %0,28\n" + "1:\n" + EX_TABLE(0b,1b) + : "+d" (rc) : "a" (addr) : "cc"); + return rc; +} + +#define ADDR2G (1ULL << 31) + +static void find_memory_chunks(struct mem_chunk chunk[]) +{ + unsigned long long memsize, rnmax, rzm; + unsigned long addr = 0, size; + int i = 0, type; + + rzm = sclp_get_rzm(); + rnmax = sclp_get_rnmax(); + memsize = rzm * rnmax; + if (!rzm) + rzm = 1ULL << 17; + if (sizeof(long) == 4) { + rzm = min(ADDR2G, rzm); + memsize = memsize ? min(ADDR2G, memsize) : ADDR2G; + } + do { + size = 0; + type = tprot(addr); + do { + size += rzm; + if (memsize && addr + size >= memsize) + break; + } while (type == tprot(addr + size)); + if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) { + chunk[i].addr = addr; + chunk[i].size = size; + chunk[i].type = type; + i++; + } + addr += size; + } while (addr < memsize && i < MEMORY_CHUNKS); +} + +void detect_memory_layout(struct mem_chunk chunk[]) +{ + unsigned long flags, cr0; + + memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk)); + if (memory_fast_detect(&chunk[0]) == 0) + return; + /* Disable IRQs, DAT and low address protection so tprot does the + * right thing and we don't get scheduled away with low address + * protection disabled. + */ + flags = __raw_local_irq_stnsm(0xf8); + __ctl_store(cr0, 0, 0); + __ctl_clear_bit(0, 28); + find_memory_chunks(chunk); + __ctl_load(cr0, 0, 0); + __raw_local_irq_ssm(flags); +} +EXPORT_SYMBOL(detect_memory_layout); diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 7920861109d..85defd01d29 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -75,46 +75,19 @@ unsigned long thread_saved_pc(struct task_struct *tsk) return sf->gprs[8]; } -/* - * Need to know about CPUs going idle? - */ -static ATOMIC_NOTIFIER_HEAD(idle_chain); DEFINE_PER_CPU(struct s390_idle_data, s390_idle); -int register_idle_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&idle_chain, nb); -} -EXPORT_SYMBOL(register_idle_notifier); - -int unregister_idle_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&idle_chain, nb); -} -EXPORT_SYMBOL(unregister_idle_notifier); - static int s390_idle_enter(void) { struct s390_idle_data *idle; - int nr_calls = 0; - void *hcpu; - int rc; - hcpu = (void *)(long)smp_processor_id(); - rc = __atomic_notifier_call_chain(&idle_chain, S390_CPU_IDLE, hcpu, -1, - &nr_calls); - if (rc == NOTIFY_BAD) { - nr_calls--; - __atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE, - hcpu, nr_calls, NULL); - return rc; - } idle = &__get_cpu_var(s390_idle); spin_lock(&idle->lock); idle->idle_count++; idle->in_idle = 1; idle->idle_enter = get_clock(); spin_unlock(&idle->lock); + vtime_stop_cpu_timer(); return NOTIFY_OK; } @@ -122,13 +95,12 @@ void s390_idle_leave(void) { struct s390_idle_data *idle; + vtime_start_cpu_timer(); idle = &__get_cpu_var(s390_idle); spin_lock(&idle->lock); idle->idle_time += get_clock() - idle->idle_enter; idle->in_idle = 0; spin_unlock(&idle->lock); - atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE, - (void *)(long) smp_processor_id()); } extern void s390_handle_mcck(void); diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 35827b9bd4d..2815bfe348a 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -33,6 +33,8 @@ #include <linux/security.h> #include <linux/audit.h> #include <linux/signal.h> +#include <linux/elf.h> +#include <linux/regset.h> #include <asm/segment.h> #include <asm/page.h> @@ -47,6 +49,11 @@ #include "compat_ptrace.h" #endif +enum s390_regset { + REGSET_GENERAL, + REGSET_FP, +}; + static void FixPerRegisters(struct task_struct *task) { @@ -126,24 +133,10 @@ ptrace_disable(struct task_struct *child) * struct user contain pad bytes that should be read as zeroes. * Lovely... */ -static int -peek_user(struct task_struct *child, addr_t addr, addr_t data) +static unsigned long __peek_user(struct task_struct *child, addr_t addr) { struct user *dummy = NULL; - addr_t offset, tmp, mask; - - /* - * Stupid gdb peeks/pokes the access registers in 64 bit with - * an alignment of 4. Programmers from hell... - */ - mask = __ADDR_MASK; -#ifdef CONFIG_64BIT - if (addr >= (addr_t) &dummy->regs.acrs && - addr < (addr_t) &dummy->regs.orig_gpr2) - mask = 3; -#endif - if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) - return -EIO; + addr_t offset, tmp; if (addr < (addr_t) &dummy->regs.acrs) { /* @@ -197,24 +190,18 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data) } else tmp = 0; - return put_user(tmp, (addr_t __user *) data); + return tmp; } -/* - * Write a word to the user area of a process at location addr. This - * operation does have an additional problem compared to peek_user. - * Stores to the program status word and on the floating point - * control register needs to get checked for validity. - */ static int -poke_user(struct task_struct *child, addr_t addr, addr_t data) +peek_user(struct task_struct *child, addr_t addr, addr_t data) { struct user *dummy = NULL; - addr_t offset, mask; + addr_t tmp, mask; /* * Stupid gdb peeks/pokes the access registers in 64 bit with - * an alignment of 4. Programmers from hell indeed... + * an alignment of 4. Programmers from hell... */ mask = __ADDR_MASK; #ifdef CONFIG_64BIT @@ -225,6 +212,21 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) return -EIO; + tmp = __peek_user(child, addr); + return put_user(tmp, (addr_t __user *) data); +} + +/* + * Write a word to the user area of a process at location addr. This + * operation does have an additional problem compared to peek_user. + * Stores to the program status word and on the floating point + * control register needs to get checked for validity. + */ +static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user *dummy = NULL; + addr_t offset; + if (addr < (addr_t) &dummy->regs.acrs) { /* * psw and gprs are stored on the stack @@ -292,6 +294,28 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data) return 0; } +static int +poke_user(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user *dummy = NULL; + addr_t mask; + + /* + * Stupid gdb peeks/pokes the access registers in 64 bit with + * an alignment of 4. Programmers from hell indeed... + */ + mask = __ADDR_MASK; +#ifdef CONFIG_64BIT + if (addr >= (addr_t) &dummy->regs.acrs && + addr < (addr_t) &dummy->regs.orig_gpr2) + mask = 3; +#endif + if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK) + return -EIO; + + return __poke_user(child, addr, data); +} + long arch_ptrace(struct task_struct *child, long request, long addr, long data) { ptrace_area parea; @@ -367,18 +391,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* * Same as peek_user but for a 31 bit program. */ -static int -peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) +static u32 __peek_user_compat(struct task_struct *child, addr_t addr) { struct user32 *dummy32 = NULL; per_struct32 *dummy_per32 = NULL; addr_t offset; __u32 tmp; - if (!test_thread_flag(TIF_31BIT) || - (addr & 3) || addr > sizeof(struct user) - 3) - return -EIO; - if (addr < (addr_t) &dummy32->regs.acrs) { /* * psw and gprs are stored on the stack @@ -435,25 +454,32 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) } else tmp = 0; + return tmp; +} + +static int peek_user_compat(struct task_struct *child, + addr_t addr, addr_t data) +{ + __u32 tmp; + + if (!test_thread_flag(TIF_31BIT) || + (addr & 3) || addr > sizeof(struct user) - 3) + return -EIO; + + tmp = __peek_user_compat(child, addr); return put_user(tmp, (__u32 __user *) data); } /* * Same as poke_user but for a 31 bit program. */ -static int -poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) +static int __poke_user_compat(struct task_struct *child, + addr_t addr, addr_t data) { struct user32 *dummy32 = NULL; per_struct32 *dummy_per32 = NULL; + __u32 tmp = (__u32) data; addr_t offset; - __u32 tmp; - - if (!test_thread_flag(TIF_31BIT) || - (addr & 3) || addr > sizeof(struct user32) - 3) - return -EIO; - - tmp = (__u32) data; if (addr < (addr_t) &dummy32->regs.acrs) { /* @@ -528,6 +554,16 @@ poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) return 0; } +static int poke_user_compat(struct task_struct *child, + addr_t addr, addr_t data) +{ + if (!test_thread_flag(TIF_31BIT) || + (addr & 3) || addr > sizeof(struct user32) - 3) + return -EIO; + + return __poke_user_compat(child, addr, data); +} + long compat_arch_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t caddr, compat_ulong_t cdata) { @@ -539,11 +575,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, switch (request) { case PTRACE_PEEKUSR: /* read the word at location addr in the USER area. */ - return peek_user_emu31(child, addr, data); + return peek_user_compat(child, addr, data); case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ - return poke_user_emu31(child, addr, data); + return poke_user_compat(child, addr, data); case PTRACE_PEEKUSR_AREA: case PTRACE_POKEUSR_AREA: @@ -555,13 +591,13 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, copied = 0; while (copied < parea.len) { if (request == PTRACE_PEEKUSR_AREA) - ret = peek_user_emu31(child, addr, data); + ret = peek_user_compat(child, addr, data); else { __u32 utmp; if (get_user(utmp, (__u32 __force __user *) data)) return -EFAULT; - ret = poke_user_emu31(child, addr, utmp); + ret = poke_user_compat(child, addr, utmp); } if (ret) return ret; @@ -610,3 +646,240 @@ syscall_trace(struct pt_regs *regs, int entryexit) regs->gprs[2], regs->orig_gpr2, regs->gprs[3], regs->gprs[4], regs->gprs[5]); } + +/* + * user_regset definitions. + */ + +static int s390_regs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (target == current) + save_access_regs(target->thread.acrs); + + if (kbuf) { + unsigned long *k = kbuf; + while (count > 0) { + *k++ = __peek_user(target, pos); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + unsigned long __user *u = ubuf; + while (count > 0) { + if (__put_user(__peek_user(target, pos), u++)) + return -EFAULT; + count -= sizeof(*u); + pos += sizeof(*u); + } + } + return 0; +} + +static int s390_regs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int rc = 0; + + if (target == current) + save_access_regs(target->thread.acrs); + + if (kbuf) { + const unsigned long *k = kbuf; + while (count > 0 && !rc) { + rc = __poke_user(target, pos, *k++); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + const unsigned long __user *u = ubuf; + while (count > 0 && !rc) { + unsigned long word; + rc = __get_user(word, u++); + if (rc) + break; + rc = __poke_user(target, pos, word); + count -= sizeof(*u); + pos += sizeof(*u); + } + } + + if (rc == 0 && target == current) + restore_access_regs(target->thread.acrs); + + return rc; +} + +static int s390_fpregs_get(struct task_struct *target, + const struct user_regset *regset, unsigned int pos, + unsigned int count, void *kbuf, void __user *ubuf) +{ + if (target == current) + save_fp_regs(&target->thread.fp_regs); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fp_regs, 0, -1); +} + +static int s390_fpregs_set(struct task_struct *target, + const struct user_regset *regset, unsigned int pos, + unsigned int count, const void *kbuf, + const void __user *ubuf) +{ + int rc = 0; + + if (target == current) + save_fp_regs(&target->thread.fp_regs); + + /* If setting FPC, must validate it first. */ + if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { + u32 fpc[2] = { target->thread.fp_regs.fpc, 0 }; + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc, + 0, offsetof(s390_fp_regs, fprs)); + if (rc) + return rc; + if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0) + return -EINVAL; + target->thread.fp_regs.fpc = fpc[0]; + } + + if (rc == 0 && count > 0) + rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.fp_regs.fprs, + offsetof(s390_fp_regs, fprs), -1); + + if (rc == 0 && target == current) + restore_fp_regs(&target->thread.fp_regs); + + return rc; +} + +static const struct user_regset s390_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = sizeof(s390_regs) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = s390_regs_get, + .set = s390_regs_set, + }, + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(s390_fp_regs) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = s390_fpregs_get, + .set = s390_fpregs_set, + }, +}; + +static const struct user_regset_view user_s390_view = { + .name = UTS_MACHINE, + .e_machine = EM_S390, + .regsets = s390_regsets, + .n = ARRAY_SIZE(s390_regsets) +}; + +#ifdef CONFIG_COMPAT +static int s390_compat_regs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (target == current) + save_access_regs(target->thread.acrs); + + if (kbuf) { + compat_ulong_t *k = kbuf; + while (count > 0) { + *k++ = __peek_user_compat(target, pos); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + compat_ulong_t __user *u = ubuf; + while (count > 0) { + if (__put_user(__peek_user_compat(target, pos), u++)) + return -EFAULT; + count -= sizeof(*u); + pos += sizeof(*u); + } + } + return 0; +} + +static int s390_compat_regs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int rc = 0; + + if (target == current) + save_access_regs(target->thread.acrs); + + if (kbuf) { + const compat_ulong_t *k = kbuf; + while (count > 0 && !rc) { + rc = __poke_user_compat(target, pos, *k++); + count -= sizeof(*k); + pos += sizeof(*k); + } + } else { + const compat_ulong_t __user *u = ubuf; + while (count > 0 && !rc) { + compat_ulong_t word; + rc = __get_user(word, u++); + if (rc) + break; + rc = __poke_user_compat(target, pos, word); + count -= sizeof(*u); + pos += sizeof(*u); + } + } + + if (rc == 0 && target == current) + restore_access_regs(target->thread.acrs); + + return rc; +} + +static const struct user_regset s390_compat_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), + .size = sizeof(compat_long_t), + .align = sizeof(compat_long_t), + .get = s390_compat_regs_get, + .set = s390_compat_regs_set, + }, + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), + .size = sizeof(compat_long_t), + .align = sizeof(compat_long_t), + .get = s390_fpregs_get, + .set = s390_fpregs_set, + }, +}; + +static const struct user_regset_view user_s390_compat_view = { + .name = "s390", + .e_machine = EM_S390, + .regsets = s390_compat_regsets, + .n = ARRAY_SIZE(s390_compat_regsets) +}; +#endif + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ +#ifdef CONFIG_COMPAT + if (test_tsk_thread_flag(task, TIF_31BIT)) + return &user_s390_compat_view; +#endif + return &user_s390_view; +} diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 2bc70b6e876..b358e18273b 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -77,7 +77,7 @@ unsigned long machine_flags; unsigned long elf_hwcap = 0; char elf_platform[ELF_PLATFORM_SIZE]; -struct mem_chunk __meminitdata memory_chunk[MEMORY_CHUNKS]; +struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS]; volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ static unsigned long __initdata memory_end; @@ -205,12 +205,6 @@ static void __init conmode_default(void) SET_CONSOLE_SCLP; #endif } - } else if (MACHINE_IS_P390) { -#if defined(CONFIG_TN3215_CONSOLE) - SET_CONSOLE_3215; -#elif defined(CONFIG_TN3270_CONSOLE) - SET_CONSOLE_3270; -#endif } else { #if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE) SET_CONSOLE_SCLP; @@ -221,18 +215,17 @@ static void __init conmode_default(void) #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) static void __init setup_zfcpdump(unsigned int console_devno) { - static char str[64]; + static char str[41]; if (ipl_info.type != IPL_TYPE_FCP_DUMP) return; if (console_devno != -1) - sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x", + sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x", ipl_info.data.fcp.dev_id.devno, console_devno); else - sprintf(str, "cio_ignore=all,!0.0.%04x", + sprintf(str, " cio_ignore=all,!0.0.%04x", ipl_info.data.fcp.dev_id.devno); - strcat(COMMAND_LINE, " "); - strcat(COMMAND_LINE, str); + strcat(boot_command_line, str); console_loglevel = 2; } #else @@ -289,32 +282,6 @@ static int __init early_parse_mem(char *p) } early_param("mem", early_parse_mem); -/* - * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes - */ -static int __init early_parse_ipldelay(char *p) -{ - unsigned long delay = 0; - - delay = simple_strtoul(p, &p, 0); - - switch (*p) { - case 's': - case 'S': - delay *= 1000000; - break; - case 'm': - case 'M': - delay *= 60 * 1000000; - } - - /* now wait for the requested amount of time */ - udelay(delay); - - return 0; -} -early_param("ipldelay", early_parse_ipldelay); - #ifdef CONFIG_S390_SWITCH_AMODE #ifdef CONFIG_PGSTE unsigned int switch_amode = 1; @@ -804,11 +771,9 @@ setup_arch(char **cmdline_p) printk("We are running native (64 bit mode)\n"); #endif /* CONFIG_64BIT */ - /* Save unparsed command line copy for /proc/cmdline */ - strlcpy(boot_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); - - *cmdline_p = COMMAND_LINE; - *(*cmdline_p + COMMAND_LINE_SIZE - 1) = '\0'; + /* Have one command line that is parsed and saved in /proc/cmdline */ + /* boot_command_line has been already set up in early.c */ + *cmdline_p = boot_command_line; ROOT_DEV = Root_RAM0; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 7aec676fefd..7418bebb547 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -3,7 +3,7 @@ * Time of day based timer functions. * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 1999, 2008 * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com), * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) @@ -31,6 +31,7 @@ #include <linux/notifier.h> #include <linux/clocksource.h> #include <linux/clockchips.h> +#include <linux/bootmem.h> #include <asm/uaccess.h> #include <asm/delay.h> #include <asm/s390_ext.h> @@ -162,7 +163,7 @@ void init_cpu_timer(void) /* Enable clock comparator timer interrupt. */ __ctl_set_bit(0,11); - /* Always allow ETR external interrupts, even without an ETR. */ + /* Always allow the timing alert external interrupt. */ __ctl_set_bit(0, 4); } @@ -170,8 +171,21 @@ static void clock_comparator_interrupt(__u16 code) { } +static void etr_timing_alert(struct etr_irq_parm *); +static void stp_timing_alert(struct stp_irq_parm *); + +static void timing_alert_interrupt(__u16 code) +{ + if (S390_lowcore.ext_params & 0x00c40000) + etr_timing_alert((struct etr_irq_parm *) + &S390_lowcore.ext_params); + if (S390_lowcore.ext_params & 0x00038000) + stp_timing_alert((struct stp_irq_parm *) + &S390_lowcore.ext_params); +} + static void etr_reset(void); -static void etr_ext_handler(__u16); +static void stp_reset(void); /* * Get the TOD clock running. @@ -181,6 +195,7 @@ static u64 __init reset_tod_clock(void) u64 time; etr_reset(); + stp_reset(); if (store_clock(&time) == 0) return time; /* TOD clock not running. Set the clock to Unix Epoch. */ @@ -231,8 +246,9 @@ void __init time_init(void) if (clocksource_register(&clocksource_tod) != 0) panic("Could not register TOD clock source"); - /* request the etr external interrupt */ - if (register_early_external_interrupt(0x1406, etr_ext_handler, + /* request the timing alert external interrupt */ + if (register_early_external_interrupt(0x1406, + timing_alert_interrupt, &ext_int_etr_cc) != 0) panic("Couldn't request external interrupt 0x1406"); @@ -245,10 +261,112 @@ void __init time_init(void) } /* + * The time is "clock". old is what we think the time is. + * Adjust the value by a multiple of jiffies and add the delta to ntp. + * "delay" is an approximation how long the synchronization took. If + * the time correction is positive, then "delay" is subtracted from + * the time difference and only the remaining part is passed to ntp. + */ +static unsigned long long adjust_time(unsigned long long old, + unsigned long long clock, + unsigned long long delay) +{ + unsigned long long delta, ticks; + struct timex adjust; + + if (clock > old) { + /* It is later than we thought. */ + delta = ticks = clock - old; + delta = ticks = (delta < delay) ? 0 : delta - delay; + delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); + adjust.offset = ticks * (1000000 / HZ); + } else { + /* It is earlier than we thought. */ + delta = ticks = old - clock; + delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); + delta = -delta; + adjust.offset = -ticks * (1000000 / HZ); + } + jiffies_timer_cc += delta; + if (adjust.offset != 0) { + printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n", + adjust.offset); + adjust.modes = ADJ_OFFSET_SINGLESHOT; + do_adjtimex(&adjust); + } + return delta; +} + +static DEFINE_PER_CPU(atomic_t, clock_sync_word); +static unsigned long clock_sync_flags; + +#define CLOCK_SYNC_HAS_ETR 0 +#define CLOCK_SYNC_HAS_STP 1 +#define CLOCK_SYNC_ETR 2 +#define CLOCK_SYNC_STP 3 + +/* + * The synchronous get_clock function. It will write the current clock + * value to the clock pointer and return 0 if the clock is in sync with + * the external time source. If the clock mode is local it will return + * -ENOSYS and -EAGAIN if the clock is not in sync with the external + * reference. + */ +int get_sync_clock(unsigned long long *clock) +{ + atomic_t *sw_ptr; + unsigned int sw0, sw1; + + sw_ptr = &get_cpu_var(clock_sync_word); + sw0 = atomic_read(sw_ptr); + *clock = get_clock(); + sw1 = atomic_read(sw_ptr); + put_cpu_var(clock_sync_sync); + if (sw0 == sw1 && (sw0 & 0x80000000U)) + /* Success: time is in sync. */ + return 0; + if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) && + !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) + return -ENOSYS; + if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) && + !test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) + return -EACCES; + return -EAGAIN; +} +EXPORT_SYMBOL(get_sync_clock); + +/* + * Make get_sync_clock return -EAGAIN. + */ +static void disable_sync_clock(void *dummy) +{ + atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); + /* + * Clear the in-sync bit 2^31. All get_sync_clock calls will + * fail until the sync bit is turned back on. In addition + * increase the "sequence" counter to avoid the race of an + * etr event and the complete recovery against get_sync_clock. + */ + atomic_clear_mask(0x80000000, sw_ptr); + atomic_inc(sw_ptr); +} + +/* + * Make get_sync_clock return 0 again. + * Needs to be called from a context disabled for preemption. + */ +static void enable_sync_clock(void) +{ + atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word); + atomic_set_mask(0x80000000, sw_ptr); +} + +/* * External Time Reference (ETR) code. */ static int etr_port0_online; static int etr_port1_online; +static int etr_steai_available; static int __init early_parse_etr(char *p) { @@ -273,12 +391,6 @@ enum etr_event { ETR_EVENT_UPDATE, }; -enum etr_flags { - ETR_FLAG_ENOSYS, - ETR_FLAG_EACCES, - ETR_FLAG_STEAI, -}; - /* * Valid bit combinations of the eacr register are (x = don't care): * e0 e1 dp p0 p1 ea es sl @@ -305,74 +417,18 @@ enum etr_flags { */ static struct etr_eacr etr_eacr; static u64 etr_tolec; /* time of last eacr update */ -static unsigned long etr_flags; static struct etr_aib etr_port0; static int etr_port0_uptodate; static struct etr_aib etr_port1; static int etr_port1_uptodate; static unsigned long etr_events; static struct timer_list etr_timer; -static DEFINE_PER_CPU(atomic_t, etr_sync_word); static void etr_timeout(unsigned long dummy); static void etr_work_fn(struct work_struct *work); static DECLARE_WORK(etr_work, etr_work_fn); /* - * The etr get_clock function. It will write the current clock value - * to the clock pointer and return 0 if the clock is in sync with the - * external time source. If the clock mode is local it will return - * -ENOSYS and -EAGAIN if the clock is not in sync with the external - * reference. This function is what ETR is all about.. - */ -int get_sync_clock(unsigned long long *clock) -{ - atomic_t *sw_ptr; - unsigned int sw0, sw1; - - sw_ptr = &get_cpu_var(etr_sync_word); - sw0 = atomic_read(sw_ptr); - *clock = get_clock(); - sw1 = atomic_read(sw_ptr); - put_cpu_var(etr_sync_sync); - if (sw0 == sw1 && (sw0 & 0x80000000U)) - /* Success: time is in sync. */ - return 0; - if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) - return -ENOSYS; - if (test_bit(ETR_FLAG_EACCES, &etr_flags)) - return -EACCES; - return -EAGAIN; -} -EXPORT_SYMBOL(get_sync_clock); - -/* - * Make get_sync_clock return -EAGAIN. - */ -static void etr_disable_sync_clock(void *dummy) -{ - atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word); - /* - * Clear the in-sync bit 2^31. All get_sync_clock calls will - * fail until the sync bit is turned back on. In addition - * increase the "sequence" counter to avoid the race of an - * etr event and the complete recovery against get_sync_clock. - */ - atomic_clear_mask(0x80000000, sw_ptr); - atomic_inc(sw_ptr); -} - -/* - * Make get_sync_clock return 0 again. - * Needs to be called from a context disabled for preemption. - */ -static void etr_enable_sync_clock(void) -{ - atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word); - atomic_set_mask(0x80000000, sw_ptr); -} - -/* * Reset ETR attachment. */ static void etr_reset(void) @@ -381,15 +437,13 @@ static void etr_reset(void) .e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0, .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0, .es = 0, .sl = 0 }; - if (etr_setr(&etr_eacr) == 0) + if (etr_setr(&etr_eacr) == 0) { etr_tolec = get_clock(); - else { - set_bit(ETR_FLAG_ENOSYS, &etr_flags); - if (etr_port0_online || etr_port1_online) { - printk(KERN_WARNING "Running on non ETR capable " - "machine, only local mode available.\n"); - etr_port0_online = etr_port1_online = 0; - } + set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); + } else if (etr_port0_online || etr_port1_online) { + printk(KERN_WARNING "Running on non ETR capable " + "machine, only local mode available.\n"); + etr_port0_online = etr_port1_online = 0; } } @@ -397,14 +451,12 @@ static int __init etr_init(void) { struct etr_aib aib; - if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) + if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) return 0; /* Check if this machine has the steai instruction. */ if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0) - set_bit(ETR_FLAG_STEAI, &etr_flags); + etr_steai_available = 1; setup_timer(&etr_timer, etr_timeout, 0UL); - if (!etr_port0_online && !etr_port1_online) - set_bit(ETR_FLAG_EACCES, &etr_flags); if (etr_port0_online) { set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); schedule_work(&etr_work); @@ -435,7 +487,8 @@ void etr_switch_to_local(void) { if (!etr_eacr.sl) return; - etr_disable_sync_clock(NULL); + if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) + disable_sync_clock(NULL); set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events); schedule_work(&etr_work); } @@ -450,23 +503,21 @@ void etr_sync_check(void) { if (!etr_eacr.es) return; - etr_disable_sync_clock(NULL); + if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) + disable_sync_clock(NULL); set_bit(ETR_EVENT_SYNC_CHECK, &etr_events); schedule_work(&etr_work); } /* - * ETR external interrupt. There are two causes: + * ETR timing alert. There are two causes: * 1) port state change, check the usability of the port * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the * sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3) * or ETR-data word 4 (edf4) has changed. */ -static void etr_ext_handler(__u16 code) +static void etr_timing_alert(struct etr_irq_parm *intparm) { - struct etr_interruption_parameter *intparm = - (struct etr_interruption_parameter *) &S390_lowcore.ext_params; - if (intparm->pc0) /* ETR port 0 state change. */ set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); @@ -591,58 +642,23 @@ static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p) return 1; } -/* - * The time is "clock". old is what we think the time is. - * Adjust the value by a multiple of jiffies and add the delta to ntp. - * "delay" is an approximation how long the synchronization took. If - * the time correction is positive, then "delay" is subtracted from - * the time difference and only the remaining part is passed to ntp. - */ -static unsigned long long etr_adjust_time(unsigned long long old, - unsigned long long clock, - unsigned long long delay) -{ - unsigned long long delta, ticks; - struct timex adjust; - - if (clock > old) { - /* It is later than we thought. */ - delta = ticks = clock - old; - delta = ticks = (delta < delay) ? 0 : delta - delay; - delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); - adjust.offset = ticks * (1000000 / HZ); - } else { - /* It is earlier than we thought. */ - delta = ticks = old - clock; - delta -= do_div(ticks, CLK_TICKS_PER_JIFFY); - delta = -delta; - adjust.offset = -ticks * (1000000 / HZ); - } - jiffies_timer_cc += delta; - if (adjust.offset != 0) { - printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n", - adjust.offset); - adjust.modes = ADJ_OFFSET_SINGLESHOT; - do_adjtimex(&adjust); - } - return delta; -} - -static struct { +struct clock_sync_data { int in_sync; unsigned long long fixup_cc; -} etr_sync; +}; -static void etr_sync_cpu_start(void *dummy) +static void clock_sync_cpu_start(void *dummy) { - etr_enable_sync_clock(); + struct clock_sync_data *sync = dummy; + + enable_sync_clock(); /* * This looks like a busy wait loop but it isn't. etr_sync_cpus * is called on all other cpus while the TOD clocks is stopped. * __udelay will stop the cpu on an enabled wait psw until the * TOD is running again. */ - while (etr_sync.in_sync == 0) { + while (sync->in_sync == 0) { __udelay(1); /* * A different cpu changes *in_sync. Therefore use @@ -650,17 +666,17 @@ static void etr_sync_cpu_start(void *dummy) */ barrier(); } - if (etr_sync.in_sync != 1) + if (sync->in_sync != 1) /* Didn't work. Clear per-cpu in sync bit again. */ - etr_disable_sync_clock(NULL); + disable_sync_clock(NULL); /* * This round of TOD syncing is done. Set the clock comparator * to the next tick and let the processor continue. */ - fixup_clock_comparator(etr_sync.fixup_cc); + fixup_clock_comparator(sync->fixup_cc); } -static void etr_sync_cpu_end(void *dummy) +static void clock_sync_cpu_end(void *dummy) { } @@ -672,6 +688,7 @@ static void etr_sync_cpu_end(void *dummy) static int etr_sync_clock(struct etr_aib *aib, int port) { struct etr_aib *sync_port; + struct clock_sync_data etr_sync; unsigned long long clock, old_clock, delay, delta; int follows; int rc; @@ -690,9 +707,9 @@ static int etr_sync_clock(struct etr_aib *aib, int port) */ memset(&etr_sync, 0, sizeof(etr_sync)); preempt_disable(); - smp_call_function(etr_sync_cpu_start, NULL, 0, 0); + smp_call_function(clock_sync_cpu_start, &etr_sync, 0, 0); local_irq_disable(); - etr_enable_sync_clock(); + enable_sync_clock(); /* Set clock to next OTE. */ __ctl_set_bit(14, 21); @@ -707,13 +724,13 @@ static int etr_sync_clock(struct etr_aib *aib, int port) /* Adjust Linux timing variables. */ delay = (unsigned long long) (aib->edf2.etv - sync_port->edf2.etv) << 32; - delta = etr_adjust_time(old_clock, clock, delay); + delta = adjust_time(old_clock, clock, delay); etr_sync.fixup_cc = delta; fixup_clock_comparator(delta); /* Verify that the clock is properly set. */ if (!etr_aib_follows(sync_port, aib, port)) { /* Didn't work. */ - etr_disable_sync_clock(NULL); + disable_sync_clock(NULL); etr_sync.in_sync = -EAGAIN; rc = -EAGAIN; } else { @@ -724,12 +741,12 @@ static int etr_sync_clock(struct etr_aib *aib, int port) /* Could not set the clock ?!? */ __ctl_clear_bit(0, 29); __ctl_clear_bit(14, 21); - etr_disable_sync_clock(NULL); + disable_sync_clock(NULL); etr_sync.in_sync = -EAGAIN; rc = -EAGAIN; } local_irq_enable(); - smp_call_function(etr_sync_cpu_end,NULL,0,0); + smp_call_function(clock_sync_cpu_end, NULL, 0, 0); preempt_enable(); return rc; } @@ -832,7 +849,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, * Do not try to get the alternate port aib if the clock * is not in sync yet. */ - if (!eacr.es) + if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es) return eacr; /* @@ -840,7 +857,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib, * the other port immediately. If only stetr is available the * data-port bit toggle has to be used. */ - if (test_bit(ETR_FLAG_STEAI, &etr_flags)) { + if (etr_steai_available) { if (eacr.p0 && !etr_port0_uptodate) { etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0); etr_port0_uptodate = 1; @@ -909,10 +926,10 @@ static void etr_work_fn(struct work_struct *work) if (!eacr.ea) { /* Both ports offline. Reset everything. */ eacr.dp = eacr.es = eacr.sl = 0; - on_each_cpu(etr_disable_sync_clock, NULL, 0, 1); + on_each_cpu(disable_sync_clock, NULL, 0, 1); del_timer_sync(&etr_timer); etr_update_eacr(eacr); - set_bit(ETR_FLAG_EACCES, &etr_flags); + clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); return; } @@ -953,7 +970,6 @@ static void etr_work_fn(struct work_struct *work) eacr.e1 = 1; sync_port = (etr_port0_uptodate && etr_port_valid(&etr_port0, 0)) ? 0 : -1; - clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) { eacr.sl = 0; eacr.e0 = 0; @@ -962,7 +978,6 @@ static void etr_work_fn(struct work_struct *work) eacr.es = 0; sync_port = (etr_port1_uptodate && etr_port_valid(&etr_port1, 1)) ? 1 : -1; - clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) { eacr.sl = 1; eacr.e0 = 1; @@ -976,7 +991,6 @@ static void etr_work_fn(struct work_struct *work) eacr.e1 = 1; sync_port = (etr_port0_uptodate && etr_port_valid(&etr_port0, 0)) ? 0 : -1; - clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) { eacr.sl = 1; eacr.e0 = 0; @@ -985,19 +999,22 @@ static void etr_work_fn(struct work_struct *work) eacr.es = 0; sync_port = (etr_port1_uptodate && etr_port_valid(&etr_port1, 1)) ? 1 : -1; - clear_bit(ETR_FLAG_EACCES, &etr_flags); } else { /* Both ports not usable. */ eacr.es = eacr.sl = 0; sync_port = -1; - set_bit(ETR_FLAG_EACCES, &etr_flags); + clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); } + if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) + eacr.es = 0; + /* * If the clock is in sync just update the eacr and return. * If there is no valid sync port wait for a port update. */ - if (eacr.es || sync_port < 0) { + if (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) || + eacr.es || sync_port < 0) { etr_update_eacr(eacr); etr_set_tolec_timeout(now); return; @@ -1018,11 +1035,13 @@ static void etr_work_fn(struct work_struct *work) * and set up a timer to try again after 0.5 seconds */ etr_update_eacr(eacr); + set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); if (now < etr_tolec + (1600000 << 12) || etr_sync_clock(&aib, sync_port) != 0) { /* Sync failed. Try again in 1/2 second. */ eacr.es = 0; etr_update_eacr(eacr); + clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags); etr_set_sync_timeout(); } else etr_set_tolec_timeout(now); @@ -1097,8 +1116,8 @@ static ssize_t etr_online_store(struct sys_device *dev, value = simple_strtoul(buf, NULL, 0); if (value != 0 && value != 1) return -EINVAL; - if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) - return -ENOSYS; + if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags)) + return -EOPNOTSUPP; if (dev == &etr_port0_dev) { if (etr_port0_online == value) return count; /* Nothing to do. */ @@ -1292,3 +1311,318 @@ out: } device_initcall(etr_init_sysfs); + +/* + * Server Time Protocol (STP) code. + */ +static int stp_online; +static struct stp_sstpi stp_info; +static void *stp_page; + +static void stp_work_fn(struct work_struct *work); +static DECLARE_WORK(stp_work, stp_work_fn); + +static int __init early_parse_stp(char *p) +{ + if (strncmp(p, "off", 3) == 0) + stp_online = 0; + else if (strncmp(p, "on", 2) == 0) + stp_online = 1; + return 0; +} +early_param("stp", early_parse_stp); + +/* + * Reset STP attachment. + */ +static void stp_reset(void) +{ + int rc; + + stp_page = alloc_bootmem_pages(PAGE_SIZE); + rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000); + if (rc == 1) + set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags); + else if (stp_online) { + printk(KERN_WARNING "Running on non STP capable machine.\n"); + free_bootmem((unsigned long) stp_page, PAGE_SIZE); + stp_page = NULL; + stp_online = 0; + } +} + +static int __init stp_init(void) +{ + if (test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online) + schedule_work(&stp_work); + return 0; +} + +arch_initcall(stp_init); + +/* + * STP timing alert. There are three causes: + * 1) timing status change + * 2) link availability change + * 3) time control parameter change + * In all three cases we are only interested in the clock source state. + * If a STP clock source is now available use it. + */ +static void stp_timing_alert(struct stp_irq_parm *intparm) +{ + if (intparm->tsc || intparm->lac || intparm->tcpc) + schedule_work(&stp_work); +} + +/* + * STP sync check machine check. This is called when the timing state + * changes from the synchronized state to the unsynchronized state. + * After a STP sync check the clock is not in sync. The machine check + * is broadcasted to all cpus at the same time. + */ +void stp_sync_check(void) +{ + if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) + return; + disable_sync_clock(NULL); + schedule_work(&stp_work); +} + +/* + * STP island condition machine check. This is called when an attached + * server attempts to communicate over an STP link and the servers + * have matching CTN ids and have a valid stratum-1 configuration + * but the configurations do not match. + */ +void stp_island_check(void) +{ + if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags)) + return; + disable_sync_clock(NULL); + schedule_work(&stp_work); +} + +/* + * STP tasklet. Check for the STP state and take over the clock + * synchronization if the STP clock source is usable. + */ +static void stp_work_fn(struct work_struct *work) +{ + struct clock_sync_data stp_sync; + unsigned long long old_clock, delta; + int rc; + + if (!stp_online) { + chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000); + return; + } + + rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0); + if (rc) + return; + + rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi)); + if (rc || stp_info.c == 0) + return; + + /* + * Catch all other cpus and make them wait until we have + * successfully synced the clock. smp_call_function will + * return after all other cpus are in clock_sync_cpu_start. + */ + memset(&stp_sync, 0, sizeof(stp_sync)); + preempt_disable(); + smp_call_function(clock_sync_cpu_start, &stp_sync, 0, 0); + local_irq_disable(); + enable_sync_clock(); + + set_bit(CLOCK_SYNC_STP, &clock_sync_flags); + if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags)) + schedule_work(&etr_work); + + rc = 0; + if (stp_info.todoff[0] || stp_info.todoff[1] || + stp_info.todoff[2] || stp_info.todoff[3] || + stp_info.tmd != 2) { + old_clock = get_clock(); + rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0); + if (rc == 0) { + delta = adjust_time(old_clock, get_clock(), 0); + fixup_clock_comparator(delta); + rc = chsc_sstpi(stp_page, &stp_info, + sizeof(struct stp_sstpi)); + if (rc == 0 && stp_info.tmd != 2) + rc = -EAGAIN; + } + } + if (rc) { + disable_sync_clock(NULL); + stp_sync.in_sync = -EAGAIN; + clear_bit(CLOCK_SYNC_STP, &clock_sync_flags); + if (etr_port0_online || etr_port1_online) + schedule_work(&etr_work); + } else + stp_sync.in_sync = 1; + + local_irq_enable(); + smp_call_function(clock_sync_cpu_end, NULL, 0, 0); + preempt_enable(); +} + +/* + * STP class sysfs interface functions + */ +static struct sysdev_class stp_sysclass = { + .name = "stp", +}; + +static ssize_t stp_ctn_id_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online) + return -ENODATA; + return sprintf(buf, "%016llx\n", + *(unsigned long long *) stp_info.ctnid); +} + +static SYSDEV_CLASS_ATTR(ctn_id, 0400, stp_ctn_id_show, NULL); + +static ssize_t stp_ctn_type_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online) + return -ENODATA; + return sprintf(buf, "%i\n", stp_info.ctn); +} + +static SYSDEV_CLASS_ATTR(ctn_type, 0400, stp_ctn_type_show, NULL); + +static ssize_t stp_dst_offset_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online || !(stp_info.vbits & 0x2000)) + return -ENODATA; + return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto); +} + +static SYSDEV_CLASS_ATTR(dst_offset, 0400, stp_dst_offset_show, NULL); + +static ssize_t stp_leap_seconds_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online || !(stp_info.vbits & 0x8000)) + return -ENODATA; + return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps); +} + +static SYSDEV_CLASS_ATTR(leap_seconds, 0400, stp_leap_seconds_show, NULL); + +static ssize_t stp_stratum_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online) + return -ENODATA; + return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum); +} + +static SYSDEV_CLASS_ATTR(stratum, 0400, stp_stratum_show, NULL); + +static ssize_t stp_time_offset_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online || !(stp_info.vbits & 0x0800)) + return -ENODATA; + return sprintf(buf, "%i\n", (int) stp_info.tto); +} + +static SYSDEV_CLASS_ATTR(time_offset, 0400, stp_time_offset_show, NULL); + +static ssize_t stp_time_zone_offset_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online || !(stp_info.vbits & 0x4000)) + return -ENODATA; + return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo); +} + +static SYSDEV_CLASS_ATTR(time_zone_offset, 0400, + stp_time_zone_offset_show, NULL); + +static ssize_t stp_timing_mode_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online) + return -ENODATA; + return sprintf(buf, "%i\n", stp_info.tmd); +} + +static SYSDEV_CLASS_ATTR(timing_mode, 0400, stp_timing_mode_show, NULL); + +static ssize_t stp_timing_state_show(struct sysdev_class *class, char *buf) +{ + if (!stp_online) + return -ENODATA; + return sprintf(buf, "%i\n", stp_info.tst); +} + +static SYSDEV_CLASS_ATTR(timing_state, 0400, stp_timing_state_show, NULL); + +static ssize_t stp_online_show(struct sysdev_class *class, char *buf) +{ + return sprintf(buf, "%i\n", stp_online); +} + +static ssize_t stp_online_store(struct sysdev_class *class, + const char *buf, size_t count) +{ + unsigned int value; + + value = simple_strtoul(buf, NULL, 0); + if (value != 0 && value != 1) + return -EINVAL; + if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) + return -EOPNOTSUPP; + stp_online = value; + schedule_work(&stp_work); + return count; +} + +/* + * Can't use SYSDEV_CLASS_ATTR because the attribute should be named + * stp/online but attr_online already exists in this file .. + */ +static struct sysdev_class_attribute attr_stp_online = { + .attr = { .name = "online", .mode = 0600 }, + .show = stp_online_show, + .store = stp_online_store, +}; + +static struct sysdev_class_attribute *stp_attributes[] = { + &attr_ctn_id, + &attr_ctn_type, + &attr_dst_offset, + &attr_leap_seconds, + &attr_stp_online, + &attr_stratum, + &attr_time_offset, + &attr_time_zone_offset, + &attr_timing_mode, + &attr_timing_state, + NULL +}; + +static int __init stp_init_sysfs(void) +{ + struct sysdev_class_attribute **attr; + int rc; + + rc = sysdev_class_register(&stp_sysclass); + if (rc) + goto out; + for (attr = stp_attributes; *attr; attr++) { + rc = sysdev_class_create_file(&stp_sysclass, *attr); + if (rc) + goto out_unreg; + } + return 0; +out_unreg: + for (; attr >= stp_attributes; attr--) + sysdev_class_remove_file(&stp_sysclass, *attr); + sysdev_class_unregister(&stp_sysclass); +out: + return rc; +} + +device_initcall(stp_init_sysfs); diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 661a0721705..212d618b009 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -313,8 +313,6 @@ void __init s390_init_cpu_topology(void) machine_has_topology_irq = 1; tl_info = alloc_bootmem_pages(PAGE_SIZE); - if (!tl_info) - goto error; info = tl_info; stsi(info, 15, 1, 2); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index ca90ee3f930..0fa5dc5d68e 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -136,7 +136,7 @@ static inline void set_vtimer(__u64 expires) } #endif -static void start_cpu_timer(void) +void vtime_start_cpu_timer(void) { struct vtimer_queue *vt_list; @@ -150,7 +150,7 @@ static void start_cpu_timer(void) set_vtimer(vt_list->idle); } -static void stop_cpu_timer(void) +void vtime_stop_cpu_timer(void) { struct vtimer_queue *vt_list; @@ -318,8 +318,7 @@ static void internal_add_vtimer(struct vtimer_list *timer) vt_list = &per_cpu(virt_cpu_timer, timer->cpu); spin_lock_irqsave(&vt_list->lock, flags); - if (timer->cpu != smp_processor_id()) - printk("internal_add_vtimer: BUG, running on wrong CPU"); + BUG_ON(timer->cpu != smp_processor_id()); /* if list is empty we only have to set the timer */ if (list_empty(&vt_list->list)) { @@ -353,25 +352,12 @@ static void internal_add_vtimer(struct vtimer_list *timer) put_cpu(); } -static inline int prepare_vtimer(struct vtimer_list *timer) +static inline void prepare_vtimer(struct vtimer_list *timer) { - if (!timer->function) { - printk("add_virt_timer: uninitialized timer\n"); - return -EINVAL; - } - - if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) { - printk("add_virt_timer: invalid timer expire value!\n"); - return -EINVAL; - } - - if (vtimer_pending(timer)) { - printk("add_virt_timer: timer pending\n"); - return -EBUSY; - } - + BUG_ON(!timer->function); + BUG_ON(!timer->expires || timer->expires > VTIMER_MAX_SLICE); + BUG_ON(vtimer_pending(timer)); timer->cpu = get_cpu(); - return 0; } /* @@ -382,10 +368,7 @@ void add_virt_timer(void *new) struct vtimer_list *timer; timer = (struct vtimer_list *)new; - - if (prepare_vtimer(timer) < 0) - return; - + prepare_vtimer(timer); timer->interval = 0; internal_add_vtimer(timer); } @@ -399,10 +382,7 @@ void add_virt_timer_periodic(void *new) struct vtimer_list *timer; timer = (struct vtimer_list *)new; - - if (prepare_vtimer(timer) < 0) - return; - + prepare_vtimer(timer); timer->interval = timer->expires; internal_add_vtimer(timer); } @@ -423,15 +403,8 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) unsigned long flags; int cpu; - if (!timer->function) { - printk("mod_virt_timer: uninitialized timer\n"); - return -EINVAL; - } - - if (!expires || expires > VTIMER_MAX_SLICE) { - printk("mod_virt_timer: invalid expire range\n"); - return -EINVAL; - } + BUG_ON(!timer->function); + BUG_ON(!expires || expires > VTIMER_MAX_SLICE); /* * This is a common optimization triggered by the @@ -444,6 +417,9 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) cpu = get_cpu(); vt_list = &per_cpu(virt_cpu_timer, cpu); + /* check if we run on the right CPU */ + BUG_ON(timer->cpu != cpu); + /* disable interrupts before test if timer is pending */ spin_lock_irqsave(&vt_list->lock, flags); @@ -458,14 +434,6 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) return 0; } - /* check if we run on the right CPU */ - if (timer->cpu != cpu) { - printk("mod_virt_timer: running on wrong CPU, check your code\n"); - spin_unlock_irqrestore(&vt_list->lock, flags); - put_cpu(); - return -EINVAL; - } - list_del_init(&timer->entry); timer->expires = expires; @@ -536,24 +504,6 @@ void init_cpu_vtimer(void) } -static int vtimer_idle_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - switch (action) { - case S390_CPU_IDLE: - stop_cpu_timer(); - break; - case S390_CPU_NOT_IDLE: - start_cpu_timer(); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block vtimer_idle_nb = { - .notifier_call = vtimer_idle_notify, -}; - void __init vtime_init(void) { /* request the cpu timer external interrupt */ @@ -561,9 +511,6 @@ void __init vtime_init(void) &ext_int_info_timer) != 0) panic("Couldn't request external interrupt 0x1005"); - if (register_idle_notifier(&vtimer_idle_nb)) - panic("Couldn't register idle notifier"); - /* Enable cpu timer interrupts on the boot cpu. */ init_cpu_vtimer(); } diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 05598649b32..388cc742005 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -202,3 +202,22 @@ void free_initrd_mem(unsigned long start, unsigned long end) } } #endif + +#ifdef CONFIG_MEMORY_HOTPLUG +int arch_add_memory(int nid, u64 start, u64 size) +{ + struct pglist_data *pgdat; + struct zone *zone; + int rc; + + pgdat = NODE_DATA(nid); + zone = pgdat->node_zones + ZONE_NORMAL; + rc = vmem_add_mapping(start, size); + if (rc) + return rc; + rc = __add_pages(zone, PFN_DOWN(start), PFN_DOWN(size)); + if (rc) + vmem_remove_mapping(start, size); + return rc; +} +#endif /* CONFIG_MEMORY_HOTPLUG */ diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c index adff76ea97c..f1a95d10595 100644 --- a/arch/x86/kernel/traps_64.c +++ b/arch/x86/kernel/traps_64.c @@ -104,30 +104,7 @@ int kstack_depth_to_print = 12; void printk_address(unsigned long address, int reliable) { -#ifdef CONFIG_KALLSYMS - unsigned long offset = 0, symsize; - const char *symname; - char *modname; - char *delim = ":"; - char namebuf[KSYM_NAME_LEN]; - char reliab[4] = ""; - - symname = kallsyms_lookup(address, &symsize, &offset, - &modname, namebuf); - if (!symname) { - printk(" [<%016lx>]\n", address); - return; - } - if (!reliable) - strcpy(reliab, "? "); - - if (!modname) - modname = delim = ""; - printk(" [<%016lx>] %s%s%s%s%s+0x%lx/0x%lx\n", - address, reliab, delim, modname, delim, symname, offset, symsize); -#else - printk(" [<%016lx>]\n", address); -#endif + printk(" [<%016lx>] %s%pS\n", address, reliable ? "": "? ", (void *) address); } static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, diff --git a/block/Kconfig b/block/Kconfig index 3e97f2bc446..1ab7c15c8d7 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -81,6 +81,18 @@ config BLK_DEV_BSG If unsure, say N. +config BLK_DEV_INTEGRITY + bool "Block layer data integrity support" + ---help--- + Some storage devices allow extra information to be + stored/retrieved to help protect the data. The block layer + data integrity option provides hooks which can be used by + filesystems to ensure better data integrity. + + Say yes here if you have a storage device that provides the + T10/SCSI Data Integrity Field or the T13/ATA External Path + Protection. If in doubt, say N. + endif # BLOCK config BLOCK_COMPAT diff --git a/block/Makefile b/block/Makefile index 5a43c7d7959..208000b0750 100644 --- a/block/Makefile +++ b/block/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \ - blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o + blk-exec.o blk-merge.o ioctl.o genhd.o scsi_ioctl.o \ + cmd-filter.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o @@ -14,3 +15,4 @@ obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o +obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/block/as-iosched.c b/block/as-iosched.c index 743f33a01a0..9735acb5b4f 100644 --- a/block/as-iosched.c +++ b/block/as-iosched.c @@ -151,6 +151,7 @@ enum arq_state { static DEFINE_PER_CPU(unsigned long, ioc_count); static struct completion *ioc_gone; +static DEFINE_SPINLOCK(ioc_gone_lock); static void as_move_to_dispatch(struct as_data *ad, struct request *rq); static void as_antic_stop(struct as_data *ad); @@ -164,8 +165,19 @@ static void free_as_io_context(struct as_io_context *aic) { kfree(aic); elv_ioc_count_dec(ioc_count); - if (ioc_gone && !elv_ioc_count_read(ioc_count)) - complete(ioc_gone); + if (ioc_gone) { + /* + * AS scheduler is exiting, grab exit lock and check + * the pending io context count. If it hits zero, + * complete ioc_gone and set it back to NULL. + */ + spin_lock(&ioc_gone_lock); + if (ioc_gone && !elv_ioc_count_read(ioc_count)) { + complete(ioc_gone); + ioc_gone = NULL; + } + spin_unlock(&ioc_gone_lock); + } } static void as_trim(struct io_context *ioc) @@ -1493,7 +1505,7 @@ static void __exit as_exit(void) /* ioc_gone's update must be visible before reading ioc_count */ smp_wmb(); if (elv_ioc_count_read(ioc_count)) - wait_for_completion(ioc_gone); + wait_for_completion(&all_gone); synchronize_rcu(); } diff --git a/block/blk-core.c b/block/blk-core.c index 1905aaba49f..dbc7f42b5d2 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -143,6 +143,10 @@ static void req_bio_endio(struct request *rq, struct bio *bio, bio->bi_size -= nbytes; bio->bi_sector += (nbytes >> 9); + + if (bio_integrity(bio)) + bio_integrity_advance(bio, nbytes); + if (bio->bi_size == 0) bio_endio(bio, error); } else { @@ -201,8 +205,7 @@ void blk_plug_device(struct request_queue *q) if (blk_queue_stopped(q)) return; - if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags)) { - __set_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags); + if (!queue_flag_test_and_set(QUEUE_FLAG_PLUGGED, q)) { mod_timer(&q->unplug_timer, jiffies + q->unplug_delay); blk_add_trace_generic(q, NULL, 0, BLK_TA_PLUG); } @@ -217,10 +220,9 @@ int blk_remove_plug(struct request_queue *q) { WARN_ON(!irqs_disabled()); - if (!test_bit(QUEUE_FLAG_PLUGGED, &q->queue_flags)) + if (!queue_flag_test_and_clear(QUEUE_FLAG_PLUGGED, q)) return 0; - queue_flag_clear(QUEUE_FLAG_PLUGGED, q); del_timer(&q->unplug_timer); return 1; } @@ -324,8 +326,7 @@ void blk_start_queue(struct request_queue *q) * one level of recursion is ok and is much faster than kicking * the unplug handling */ - if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) { - queue_flag_set(QUEUE_FLAG_REENTER, q); + if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { @@ -390,8 +391,7 @@ void __blk_run_queue(struct request_queue *q) * handling reinvoke the handler shortly if we already got there. */ if (!elv_queue_empty(q)) { - if (!test_bit(QUEUE_FLAG_REENTER, &q->queue_flags)) { - queue_flag_set(QUEUE_FLAG_REENTER, q); + if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { @@ -1381,6 +1381,9 @@ end_io: */ blk_partition_remap(bio); + if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) + goto end_io; + if (old_sector != -1) blk_add_trace_remap(q, bio, old_dev, bio->bi_sector, old_sector); diff --git a/block/blk-integrity.c b/block/blk-integrity.c new file mode 100644 index 00000000000..3f1a8478cc3 --- /dev/null +++ b/block/blk-integrity.c @@ -0,0 +1,381 @@ +/* + * blk-integrity.c - Block layer data integrity extensions + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen <martin.petersen@oracle.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include <linux/blkdev.h> +#include <linux/mempool.h> +#include <linux/bio.h> +#include <linux/scatterlist.h> + +#include "blk.h" + +static struct kmem_cache *integrity_cachep; + +/** + * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements + * @rq: request with integrity metadata attached + * + * Description: Returns the number of elements required in a + * scatterlist corresponding to the integrity metadata in a request. + */ +int blk_rq_count_integrity_sg(struct request *rq) +{ + struct bio_vec *iv, *ivprv; + struct req_iterator iter; + unsigned int segments; + + ivprv = NULL; + segments = 0; + + rq_for_each_integrity_segment(iv, rq, iter) { + + if (!ivprv || !BIOVEC_PHYS_MERGEABLE(ivprv, iv)) + segments++; + + ivprv = iv; + } + + return segments; +} +EXPORT_SYMBOL(blk_rq_count_integrity_sg); + +/** + * blk_rq_map_integrity_sg - Map integrity metadata into a scatterlist + * @rq: request with integrity metadata attached + * @sglist: target scatterlist + * + * Description: Map the integrity vectors in request into a + * scatterlist. The scatterlist must be big enough to hold all + * elements. I.e. sized using blk_rq_count_integrity_sg(). + */ +int blk_rq_map_integrity_sg(struct request *rq, struct scatterlist *sglist) +{ + struct bio_vec *iv, *ivprv; + struct req_iterator iter; + struct scatterlist *sg; + unsigned int segments; + + ivprv = NULL; + sg = NULL; + segments = 0; + + rq_for_each_integrity_segment(iv, rq, iter) { + + if (ivprv) { + if (!BIOVEC_PHYS_MERGEABLE(ivprv, iv)) + goto new_segment; + + sg->length += iv->bv_len; + } else { +new_segment: + if (!sg) + sg = sglist; + else { + sg->page_link &= ~0x02; + sg = sg_next(sg); + } + + sg_set_page(sg, iv->bv_page, iv->bv_len, iv->bv_offset); + segments++; + } + + ivprv = iv; + } + + if (sg) + sg_mark_end(sg); + + return segments; +} +EXPORT_SYMBOL(blk_rq_map_integrity_sg); + +/** + * blk_integrity_compare - Compare integrity profile of two block devices + * @b1: Device to compare + * @b2: Device to compare + * + * Description: Meta-devices like DM and MD need to verify that all + * sub-devices use the same integrity format before advertising to + * upper layers that they can send/receive integrity metadata. This + * function can be used to check whether two block devices have + * compatible integrity formats. + */ +int blk_integrity_compare(struct block_device *bd1, struct block_device *bd2) +{ + struct blk_integrity *b1 = bd1->bd_disk->integrity; + struct blk_integrity *b2 = bd2->bd_disk->integrity; + + BUG_ON(bd1->bd_disk == NULL); + BUG_ON(bd2->bd_disk == NULL); + + if (!b1 || !b2) + return 0; + + if (b1->sector_size != b2->sector_size) { + printk(KERN_ERR "%s: %s/%s sector sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->sector_size, b2->sector_size); + return -1; + } + + if (b1->tuple_size != b2->tuple_size) { + printk(KERN_ERR "%s: %s/%s tuple sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->tuple_size, b2->tuple_size); + return -1; + } + + if (b1->tag_size && b2->tag_size && (b1->tag_size != b2->tag_size)) { + printk(KERN_ERR "%s: %s/%s tag sz %u != %u\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->tag_size, b2->tag_size); + return -1; + } + + if (strcmp(b1->name, b2->name)) { + printk(KERN_ERR "%s: %s/%s type %s != %s\n", __func__, + bd1->bd_disk->disk_name, bd2->bd_disk->disk_name, + b1->name, b2->name); + return -1; + } + + return 0; +} +EXPORT_SYMBOL(blk_integrity_compare); + +struct integrity_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct blk_integrity *, char *); + ssize_t (*store)(struct blk_integrity *, const char *, size_t); +}; + +static ssize_t integrity_attr_show(struct kobject *kobj, struct attribute *attr, + char *page) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + struct integrity_sysfs_entry *entry = + container_of(attr, struct integrity_sysfs_entry, attr); + + return entry->show(bi, page); +} + +static ssize_t integrity_attr_store(struct kobject *kobj, + struct attribute *attr, const char *page, + size_t count) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + struct integrity_sysfs_entry *entry = + container_of(attr, struct integrity_sysfs_entry, attr); + ssize_t ret = 0; + + if (entry->store) + ret = entry->store(bi, page, count); + + return ret; +} + +static ssize_t integrity_format_show(struct blk_integrity *bi, char *page) +{ + if (bi != NULL && bi->name != NULL) + return sprintf(page, "%s\n", bi->name); + else + return sprintf(page, "none\n"); +} + +static ssize_t integrity_tag_size_show(struct blk_integrity *bi, char *page) +{ + if (bi != NULL) + return sprintf(page, "%u\n", bi->tag_size); + else + return sprintf(page, "0\n"); +} + +static ssize_t integrity_read_store(struct blk_integrity *bi, + const char *page, size_t count) +{ + char *p = (char *) page; + unsigned long val = simple_strtoul(p, &p, 10); + + if (val) + bi->flags |= INTEGRITY_FLAG_READ; + else + bi->flags &= ~INTEGRITY_FLAG_READ; + + return count; +} + +static ssize_t integrity_read_show(struct blk_integrity *bi, char *page) +{ + return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_READ) != 0); +} + +static ssize_t integrity_write_store(struct blk_integrity *bi, + const char *page, size_t count) +{ + char *p = (char *) page; + unsigned long val = simple_strtoul(p, &p, 10); + + if (val) + bi->flags |= INTEGRITY_FLAG_WRITE; + else + bi->flags &= ~INTEGRITY_FLAG_WRITE; + + return count; +} + +static ssize_t integrity_write_show(struct blk_integrity *bi, char *page) +{ + return sprintf(page, "%d\n", (bi->flags & INTEGRITY_FLAG_WRITE) != 0); +} + +static struct integrity_sysfs_entry integrity_format_entry = { + .attr = { .name = "format", .mode = S_IRUGO }, + .show = integrity_format_show, +}; + +static struct integrity_sysfs_entry integrity_tag_size_entry = { + .attr = { .name = "tag_size", .mode = S_IRUGO }, + .show = integrity_tag_size_show, +}; + +static struct integrity_sysfs_entry integrity_read_entry = { + .attr = { .name = "read_verify", .mode = S_IRUGO | S_IWUSR }, + .show = integrity_read_show, + .store = integrity_read_store, +}; + +static struct integrity_sysfs_entry integrity_write_entry = { + .attr = { .name = "write_generate", .mode = S_IRUGO | S_IWUSR }, + .show = integrity_write_show, + .store = integrity_write_store, +}; + +static struct attribute *integrity_attrs[] = { + &integrity_format_entry.attr, + &integrity_tag_size_entry.attr, + &integrity_read_entry.attr, + &integrity_write_entry.attr, + NULL, +}; + +static struct sysfs_ops integrity_ops = { + .show = &integrity_attr_show, + .store = &integrity_attr_store, +}; + +static int __init blk_dev_integrity_init(void) +{ + integrity_cachep = kmem_cache_create("blkdev_integrity", + sizeof(struct blk_integrity), + 0, SLAB_PANIC, NULL); + return 0; +} +subsys_initcall(blk_dev_integrity_init); + +static void blk_integrity_release(struct kobject *kobj) +{ + struct blk_integrity *bi = + container_of(kobj, struct blk_integrity, kobj); + + kmem_cache_free(integrity_cachep, bi); +} + +static struct kobj_type integrity_ktype = { + .default_attrs = integrity_attrs, + .sysfs_ops = &integrity_ops, + .release = blk_integrity_release, +}; + +/** + * blk_integrity_register - Register a gendisk as being integrity-capable + * @disk: struct gendisk pointer to make integrity-aware + * @template: integrity profile + * + * Description: When a device needs to advertise itself as being able + * to send/receive integrity metadata it must use this function to + * register the capability with the block layer. The template is a + * blk_integrity struct with values appropriate for the underlying + * hardware. See Documentation/block/data-integrity.txt. + */ +int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template) +{ + struct blk_integrity *bi; + + BUG_ON(disk == NULL); + BUG_ON(template == NULL); + + if (disk->integrity == NULL) { + bi = kmem_cache_alloc(integrity_cachep, + GFP_KERNEL | __GFP_ZERO); + if (!bi) + return -1; + + if (kobject_init_and_add(&bi->kobj, &integrity_ktype, + &disk->dev.kobj, "%s", "integrity")) { + kmem_cache_free(integrity_cachep, bi); + return -1; + } + + kobject_uevent(&bi->kobj, KOBJ_ADD); + + bi->flags |= INTEGRITY_FLAG_READ | INTEGRITY_FLAG_WRITE; + bi->sector_size = disk->queue->hardsect_size; + disk->integrity = bi; + } else + bi = disk->integrity; + + /* Use the provided profile as template */ + bi->name = template->name; + bi->generate_fn = template->generate_fn; + bi->verify_fn = template->verify_fn; + bi->tuple_size = template->tuple_size; + bi->set_tag_fn = template->set_tag_fn; + bi->get_tag_fn = template->get_tag_fn; + bi->tag_size = template->tag_size; + + return 0; +} +EXPORT_SYMBOL(blk_integrity_register); + +/** + * blk_integrity_unregister - Remove block integrity profile + * @disk: disk whose integrity profile to deallocate + * + * Description: This function frees all memory used by the block + * integrity profile. To be called at device teardown. + */ +void blk_integrity_unregister(struct gendisk *disk) +{ + struct blk_integrity *bi; + + if (!disk || !disk->integrity) + return; + + bi = disk->integrity; + + kobject_uevent(&bi->kobj, KOBJ_REMOVE); + kobject_del(&bi->kobj); + kobject_put(&disk->dev.kobj); + kmem_cache_free(integrity_cachep, bi); +} +EXPORT_SYMBOL(blk_integrity_unregister); diff --git a/block/blk-map.c b/block/blk-map.c index 0b1af5a3537..ddd96fb11a7 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -210,6 +210,7 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, if (!bio_flagged(bio, BIO_USER_MAPPED)) rq->cmd_flags |= REQ_COPY_USER; + blk_queue_bounce(q, &bio); bio_get(bio); blk_rq_bio_prep(q, rq, bio); rq->buffer = rq->data = NULL; @@ -268,6 +269,7 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, int reading = rq_data_dir(rq) == READ; int do_copy = 0; struct bio *bio; + unsigned long stack_mask = ~(THREAD_SIZE - 1); if (len > (q->max_hw_sectors << 9)) return -EINVAL; @@ -278,6 +280,10 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, alignment = queue_dma_alignment(q) | q->dma_pad_mask; do_copy = ((kaddr & alignment) || (len & alignment)); + if (!((kaddr & stack_mask) ^ + ((unsigned long)current->stack & stack_mask))) + do_copy = 1; + if (do_copy) bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading); else diff --git a/block/blk-merge.c b/block/blk-merge.c index 651136aae76..5efc9e7a68b 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -441,6 +441,9 @@ static int attempt_merge(struct request_queue *q, struct request *req, || next->special) return 0; + if (blk_integrity_rq(req) != blk_integrity_rq(next)) + return 0; + /* * If we are allowed to merge, then append bio list * from next to rq and release next. merge_requests_fn diff --git a/block/blk-settings.c b/block/blk-settings.c index 8dd86418f35..dfc77012843 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -302,11 +302,10 @@ EXPORT_SYMBOL(blk_queue_stack_limits); * @q: the request queue for the device * @mask: pad mask * - * Set pad mask. Direct IO requests are padded to the mask specified. + * Set dma pad mask. * - * Appending pad buffer to a request modifies ->data_len such that it - * includes the pad buffer. The original requested data length can be - * obtained using blk_rq_raw_data_len(). + * Appending pad buffer to a request modifies the last entry of a + * scatter list such that it includes the pad buffer. **/ void blk_queue_dma_pad(struct request_queue *q, unsigned int mask) { @@ -315,6 +314,23 @@ void blk_queue_dma_pad(struct request_queue *q, unsigned int mask) EXPORT_SYMBOL(blk_queue_dma_pad); /** + * blk_queue_update_dma_pad - update pad mask + * @q: the request queue for the device + * @mask: pad mask + * + * Update dma pad mask. + * + * Appending pad buffer to a request modifies the last entry of a + * scatter list such that it includes the pad buffer. + **/ +void blk_queue_update_dma_pad(struct request_queue *q, unsigned int mask) +{ + if (mask > q->dma_pad_mask) + q->dma_pad_mask = mask; +} +EXPORT_SYMBOL(blk_queue_update_dma_pad); + +/** * blk_queue_dma_drain - Set up a drain buffer for excess dma. * @q: the request queue for the device * @dma_drain_needed: fn which returns non-zero if drain is necessary diff --git a/block/blk.h b/block/blk.h index 59776ab4742..c79f30e1df5 100644 --- a/block/blk.h +++ b/block/blk.h @@ -51,4 +51,12 @@ static inline int queue_congestion_off_threshold(struct request_queue *q) return q->nr_congestion_off; } +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +#define rq_for_each_integrity_segment(bvl, _rq, _iter) \ + __rq_for_each_bio(_iter.bio, _rq) \ + bip_for_each_vec(bvl, _iter.bio->bi_integrity, _iter.i) + +#endif /* BLK_DEV_INTEGRITY */ + #endif diff --git a/block/blktrace.c b/block/blktrace.c index 8d3a2778026..eb9651ccb24 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -244,6 +244,7 @@ err: static void blk_trace_cleanup(struct blk_trace *bt) { relay_close(bt->rchan); + debugfs_remove(bt->msg_file); debugfs_remove(bt->dropped_file); blk_remove_tree(bt->dir); free_percpu(bt->sequence); @@ -291,6 +292,44 @@ static const struct file_operations blk_dropped_fops = { .read = blk_dropped_read, }; +static int blk_msg_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + + return 0; +} + +static ssize_t blk_msg_write(struct file *filp, const char __user *buffer, + size_t count, loff_t *ppos) +{ + char *msg; + struct blk_trace *bt; + + if (count > BLK_TN_MAX_MSG) + return -EINVAL; + + msg = kmalloc(count, GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + if (copy_from_user(msg, buffer, count)) { + kfree(msg); + return -EFAULT; + } + + bt = filp->private_data; + __trace_note_message(bt, "%s", msg); + kfree(msg); + + return count; +} + +static const struct file_operations blk_msg_fops = { + .owner = THIS_MODULE, + .open = blk_msg_open, + .write = blk_msg_write, +}; + /* * Keep track of how many times we encountered a full subbuffer, to aid * the user space app in telling how many lost events there were. @@ -380,6 +419,10 @@ int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev, if (!bt->dropped_file) goto err; + bt->msg_file = debugfs_create_file("msg", 0222, dir, bt, &blk_msg_fops); + if (!bt->msg_file) + goto err; + bt->rchan = relay_open("trace", dir, buts->buf_size, buts->buf_nr, &blk_relay_callbacks, bt); if (!bt->rchan) @@ -409,6 +452,8 @@ err: if (dir) blk_remove_tree(dir); if (bt) { + if (bt->msg_file) + debugfs_remove(bt->msg_file); if (bt->dropped_file) debugfs_remove(bt->dropped_file); free_percpu(bt->sequence); diff --git a/block/bsg.c b/block/bsg.c index 54d617f7df3..93e757d7174 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -44,11 +44,12 @@ struct bsg_device { char name[BUS_ID_SIZE]; int max_queue; unsigned long flags; + struct blk_scsi_cmd_filter *cmd_filter; + mode_t *f_mode; }; enum { BSG_F_BLOCK = 1, - BSG_F_WRITE_PERM = 2, }; #define BSG_DEFAULT_CMDS 64 @@ -172,7 +173,7 @@ unlock: } static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_v4 *hdr, int has_write_perm) + struct sg_io_v4 *hdr, struct bsg_device *bd) { if (hdr->request_len > BLK_MAX_CDB) { rq->cmd = kzalloc(hdr->request_len, GFP_KERNEL); @@ -185,7 +186,8 @@ static int blk_fill_sgv4_hdr_rq(struct request_queue *q, struct request *rq, return -EFAULT; if (hdr->subprotocol == BSG_SUB_PROTOCOL_SCSI_CMD) { - if (blk_verify_command(rq->cmd, has_write_perm)) + if (blk_cmd_filter_verify_command(bd->cmd_filter, rq->cmd, + bd->f_mode)) return -EPERM; } else if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -263,8 +265,7 @@ bsg_map_hdr(struct bsg_device *bd, struct sg_io_v4 *hdr) rq = blk_get_request(q, rw, GFP_KERNEL); if (!rq) return ERR_PTR(-ENOMEM); - ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, test_bit(BSG_F_WRITE_PERM, - &bd->flags)); + ret = blk_fill_sgv4_hdr_rq(q, rq, hdr, bd); if (ret) goto out; @@ -566,12 +567,23 @@ static inline void bsg_set_block(struct bsg_device *bd, struct file *file) set_bit(BSG_F_BLOCK, &bd->flags); } -static inline void bsg_set_write_perm(struct bsg_device *bd, struct file *file) +static void bsg_set_cmd_filter(struct bsg_device *bd, + struct file *file) { - if (file->f_mode & FMODE_WRITE) - set_bit(BSG_F_WRITE_PERM, &bd->flags); - else - clear_bit(BSG_F_WRITE_PERM, &bd->flags); + struct inode *inode; + struct gendisk *disk; + + if (!file) + return; + + inode = file->f_dentry->d_inode; + if (!inode) + return; + + disk = inode->i_bdev->bd_disk; + + bd->cmd_filter = &disk->cmd_filter; + bd->f_mode = &file->f_mode; } /* @@ -595,6 +607,8 @@ bsg_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) dprintk("%s: read %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); + bsg_set_cmd_filter(bd, file); + bytes_read = 0; ret = __bsg_read(buf, count, bd, NULL, &bytes_read); *ppos = bytes_read; @@ -668,7 +682,7 @@ bsg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) dprintk("%s: write %Zd bytes\n", bd->name, count); bsg_set_block(bd, file); - bsg_set_write_perm(bd, file); + bsg_set_cmd_filter(bd, file); bytes_written = 0; ret = __bsg_write(bd, buf, count, &bytes_written); @@ -772,7 +786,9 @@ static struct bsg_device *bsg_add_device(struct inode *inode, } bd->queue = rq; + bsg_set_block(bd, file); + bsg_set_cmd_filter(bd, file); atomic_set(&bd->ref_count, 1); mutex_lock(&bsg_mutex); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d01b411c72f..1e2aff812ee 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -11,6 +11,7 @@ #include <linux/elevator.h> #include <linux/rbtree.h> #include <linux/ioprio.h> +#include <linux/blktrace_api.h> /* * tunables @@ -41,13 +42,14 @@ static int cfq_slice_idle = HZ / 125; #define RQ_CIC(rq) \ ((struct cfq_io_context *) (rq)->elevator_private) -#define RQ_CFQQ(rq) ((rq)->elevator_private2) +#define RQ_CFQQ(rq) (struct cfq_queue *) ((rq)->elevator_private2) static struct kmem_cache *cfq_pool; static struct kmem_cache *cfq_ioc_pool; static DEFINE_PER_CPU(unsigned long, ioc_count); static struct completion *ioc_gone; +static DEFINE_SPINLOCK(ioc_gone_lock); #define CFQ_PRIO_LISTS IOPRIO_BE_NR #define cfq_class_idle(cfqq) ((cfqq)->ioprio_class == IOPRIO_CLASS_IDLE) @@ -155,6 +157,7 @@ struct cfq_queue { unsigned short ioprio, org_ioprio; unsigned short ioprio_class, org_ioprio_class; + pid_t pid; }; enum cfqq_state_flags { @@ -198,6 +201,11 @@ CFQ_CFQQ_FNS(slice_new); CFQ_CFQQ_FNS(sync); #undef CFQ_CFQQ_FNS +#define cfq_log_cfqq(cfqd, cfqq, fmt, args...) \ + blk_add_trace_msg((cfqd)->queue, "cfq%d " fmt, (cfqq)->pid, ##args) +#define cfq_log(cfqd, fmt, args...) \ + blk_add_trace_msg((cfqd)->queue, "cfq " fmt, ##args) + static void cfq_dispatch_insert(struct request_queue *, struct request *); static struct cfq_queue *cfq_get_queue(struct cfq_data *, int, struct io_context *, gfp_t); @@ -234,8 +242,10 @@ static inline int cfq_bio_sync(struct bio *bio) */ static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) { - if (cfqd->busy_queues) + if (cfqd->busy_queues) { + cfq_log(cfqd, "schedule dispatch"); kblockd_schedule_work(&cfqd->unplug_work); + } } static int cfq_queue_empty(struct request_queue *q) @@ -270,6 +280,7 @@ static inline void cfq_set_prio_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) { cfqq->slice_end = cfq_prio_to_slice(cfqd, cfqq) + jiffies; + cfq_log_cfqq(cfqd, cfqq, "set_slice=%lu", cfqq->slice_end - jiffies); } /* @@ -539,6 +550,7 @@ static void cfq_resort_rr_list(struct cfq_data *cfqd, struct cfq_queue *cfqq) */ static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + cfq_log_cfqq(cfqd, cfqq, "add_to_rr"); BUG_ON(cfq_cfqq_on_rr(cfqq)); cfq_mark_cfqq_on_rr(cfqq); cfqd->busy_queues++; @@ -552,6 +564,7 @@ static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) */ static void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + cfq_log_cfqq(cfqd, cfqq, "del_from_rr"); BUG_ON(!cfq_cfqq_on_rr(cfqq)); cfq_clear_cfqq_on_rr(cfqq); @@ -638,6 +651,8 @@ static void cfq_activate_request(struct request_queue *q, struct request *rq) struct cfq_data *cfqd = q->elevator->elevator_data; cfqd->rq_in_driver++; + cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "activate rq, drv=%d", + cfqd->rq_in_driver); /* * If the depth is larger 1, it really could be queueing. But lets @@ -657,6 +672,8 @@ static void cfq_deactivate_request(struct request_queue *q, struct request *rq) WARN_ON(!cfqd->rq_in_driver); cfqd->rq_in_driver--; + cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "deactivate rq, drv=%d", + cfqd->rq_in_driver); } static void cfq_remove_request(struct request *rq) @@ -746,6 +763,7 @@ static void __cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { if (cfqq) { + cfq_log_cfqq(cfqd, cfqq, "set_active"); cfqq->slice_end = 0; cfq_clear_cfqq_must_alloc_slice(cfqq); cfq_clear_cfqq_fifo_expire(cfqq); @@ -763,6 +781,8 @@ static void __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, int timed_out) { + cfq_log_cfqq(cfqd, cfqq, "slice expired t=%d", timed_out); + if (cfq_cfqq_wait_request(cfqq)) del_timer(&cfqd->idle_slice_timer); @@ -772,8 +792,10 @@ __cfq_slice_expired(struct cfq_data *cfqd, struct cfq_queue *cfqq, /* * store what was left of this slice, if the queue idled/timed out */ - if (timed_out && !cfq_cfqq_slice_new(cfqq)) + if (timed_out && !cfq_cfqq_slice_new(cfqq)) { cfqq->slice_resid = cfqq->slice_end - jiffies; + cfq_log_cfqq(cfqd, cfqq, "resid=%ld", cfqq->slice_resid); + } cfq_resort_rr_list(cfqd, cfqq); @@ -866,6 +888,12 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) return; /* + * still requests with the driver, don't idle + */ + if (cfqd->rq_in_driver) + return; + + /* * task has exited, don't wait */ cic = cfqd->active_cic; @@ -892,6 +920,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd) sl = min(sl, msecs_to_jiffies(CFQ_MIN_TT)); mod_timer(&cfqd->idle_slice_timer, jiffies + sl); + cfq_log(cfqd, "arm_idle: %lu", sl); } /* @@ -902,6 +931,8 @@ static void cfq_dispatch_insert(struct request_queue *q, struct request *rq) struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq = RQ_CFQQ(rq); + cfq_log_cfqq(cfqd, cfqq, "dispatch_insert"); + cfq_remove_request(rq); cfqq->dispatched++; elv_dispatch_sort(q, rq); @@ -931,8 +962,9 @@ static struct request *cfq_check_fifo(struct cfq_queue *cfqq) rq = rq_entry_fifo(cfqq->fifo.next); if (time_before(jiffies, rq->start_time + cfqd->cfq_fifo_expire[fifo])) - return NULL; + rq = NULL; + cfq_log_cfqq(cfqd, cfqq, "fifo=%p", rq); return rq; } @@ -1072,6 +1104,7 @@ static int cfq_forced_dispatch(struct cfq_data *cfqd) BUG_ON(cfqd->busy_queues); + cfq_log(cfqd, "forced_dispatch=%d\n", dispatched); return dispatched; } @@ -1112,6 +1145,7 @@ static int cfq_dispatch_requests(struct request_queue *q, int force) dispatched += __cfq_dispatch_requests(cfqd, cfqq, max_dispatch); } + cfq_log(cfqd, "dispatched=%d", dispatched); return dispatched; } @@ -1130,6 +1164,7 @@ static void cfq_put_queue(struct cfq_queue *cfqq) if (!atomic_dec_and_test(&cfqq->ref)) return; + cfq_log_cfqq(cfqd, cfqq, "put_queue"); BUG_ON(rb_first(&cfqq->sort_list)); BUG_ON(cfqq->allocated[READ] + cfqq->allocated[WRITE]); BUG_ON(cfq_cfqq_on_rr(cfqq)); @@ -1177,8 +1212,19 @@ static void cfq_cic_free_rcu(struct rcu_head *head) kmem_cache_free(cfq_ioc_pool, cic); elv_ioc_count_dec(ioc_count); - if (ioc_gone && !elv_ioc_count_read(ioc_count)) - complete(ioc_gone); + if (ioc_gone) { + /* + * CFQ scheduler is exiting, grab exit lock and check + * the pending io context count. If it hits zero, + * complete ioc_gone and set it back to NULL + */ + spin_lock(&ioc_gone_lock); + if (ioc_gone && !elv_ioc_count_read(ioc_count)) { + complete(ioc_gone); + ioc_gone = NULL; + } + spin_unlock(&ioc_gone_lock); + } } static void cfq_cic_free(struct cfq_io_context *cic) @@ -1427,6 +1473,8 @@ retry: cfq_mark_cfqq_idle_window(cfqq); cfq_mark_cfqq_sync(cfqq); } + cfqq->pid = current->pid; + cfq_log_cfqq(cfqd, cfqq, "alloced"); } if (new_cfqq) @@ -1675,7 +1723,7 @@ static void cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_io_context *cic) { - int enable_idle; + int old_idle, enable_idle; /* * Don't idle for async or idle io prio class @@ -1683,7 +1731,7 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, if (!cfq_cfqq_sync(cfqq) || cfq_class_idle(cfqq)) return; - enable_idle = cfq_cfqq_idle_window(cfqq); + enable_idle = old_idle = cfq_cfqq_idle_window(cfqq); if (!atomic_read(&cic->ioc->nr_tasks) || !cfqd->cfq_slice_idle || (cfqd->hw_tag && CIC_SEEKY(cic))) @@ -1695,10 +1743,13 @@ cfq_update_idle_window(struct cfq_data *cfqd, struct cfq_queue *cfqq, enable_idle = 1; } - if (enable_idle) - cfq_mark_cfqq_idle_window(cfqq); - else - cfq_clear_cfqq_idle_window(cfqq); + if (old_idle != enable_idle) { + cfq_log_cfqq(cfqd, cfqq, "idle=%d", enable_idle); + if (enable_idle) + cfq_mark_cfqq_idle_window(cfqq); + else + cfq_clear_cfqq_idle_window(cfqq); + } } /* @@ -1757,6 +1808,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, */ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { + cfq_log_cfqq(cfqd, cfqq, "preempt"); cfq_slice_expired(cfqd, 1); /* @@ -1818,6 +1870,7 @@ static void cfq_insert_request(struct request_queue *q, struct request *rq) struct cfq_data *cfqd = q->elevator->elevator_data; struct cfq_queue *cfqq = RQ_CFQQ(rq); + cfq_log_cfqq(cfqd, cfqq, "insert_request"); cfq_init_prio_data(cfqq, RQ_CIC(rq)->ioc); cfq_add_rq_rb(rq); @@ -1835,6 +1888,7 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq) unsigned long now; now = jiffies; + cfq_log_cfqq(cfqd, cfqq, "complete"); WARN_ON(!cfqd->rq_in_driver); WARN_ON(!cfqq->dispatched); @@ -2004,6 +2058,7 @@ queue_fail: cfq_schedule_dispatch(cfqd); spin_unlock_irqrestore(q->queue_lock, flags); + cfq_log(cfqd, "set_request fail"); return 1; } @@ -2029,6 +2084,8 @@ static void cfq_idle_slice_timer(unsigned long data) unsigned long flags; int timed_out = 1; + cfq_log(cfqd, "idle timer fired"); + spin_lock_irqsave(cfqd->queue->queue_lock, flags); cfqq = cfqd->active_queue; @@ -2317,7 +2374,7 @@ static void __exit cfq_exit(void) * pending RCU callbacks */ if (elv_ioc_count_read(ioc_count)) - wait_for_completion(ioc_gone); + wait_for_completion(&all_gone); cfq_slab_kill(); } diff --git a/block/cmd-filter.c b/block/cmd-filter.c new file mode 100644 index 00000000000..eec4404fd35 --- /dev/null +++ b/block/cmd-filter.c @@ -0,0 +1,334 @@ +/* + * Copyright 2004 Peter M. Jones <pjones@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public Licens + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- + * + */ + +#include <linux/list.h> +#include <linux/genhd.h> +#include <linux/spinlock.h> +#include <linux/parser.h> +#include <linux/capability.h> +#include <linux/bitops.h> + +#include <scsi/scsi.h> +#include <linux/cdrom.h> + +int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, mode_t *f_mode) +{ + /* root can do any command. */ + if (capable(CAP_SYS_RAWIO)) + return 0; + + /* if there's no filter set, assume we're filtering everything out */ + if (!filter) + return -EPERM; + + /* Anybody who can open the device can do a read-safe command */ + if (test_bit(cmd[0], filter->read_ok)) + return 0; + + /* Write-safe commands require a writable open */ + if (test_bit(cmd[0], filter->write_ok) && (*f_mode & FMODE_WRITE)) + return 0; + + return -EPERM; +} +EXPORT_SYMBOL(blk_cmd_filter_verify_command); + +int blk_verify_command(struct file *file, unsigned char *cmd) +{ + struct gendisk *disk; + struct inode *inode; + + if (!file) + return -EINVAL; + + inode = file->f_dentry->d_inode; + if (!inode) + return -EINVAL; + + disk = inode->i_bdev->bd_disk; + + return blk_cmd_filter_verify_command(&disk->cmd_filter, + cmd, &file->f_mode); +} +EXPORT_SYMBOL(blk_verify_command); + +/* and now, the sysfs stuff */ +static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page, + int rw) +{ + char *npage = page; + unsigned long *okbits; + int i; + + if (rw == READ) + okbits = filter->read_ok; + else + okbits = filter->write_ok; + + for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) { + if (test_bit(i, okbits)) { + sprintf(npage, "%02x", i); + npage += 2; + if (i < BLK_SCSI_MAX_CMDS - 1) + sprintf(npage++, " "); + } + } + + if (npage != page) + npage += sprintf(npage, "\n"); + + return npage - page; +} + +static ssize_t rcf_readcmds_show(struct blk_scsi_cmd_filter *filter, char *page) +{ + return rcf_cmds_show(filter, page, READ); +} + +static ssize_t rcf_writecmds_show(struct blk_scsi_cmd_filter *filter, + char *page) +{ + return rcf_cmds_show(filter, page, WRITE); +} + +static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count, int rw) +{ + ssize_t ret = 0; + unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; + int cmd, status, len; + substring_t ss; + + memset(&okbits, 0, sizeof(okbits)); + + for (len = strlen(page); len > 0; len -= 3) { + if (len < 2) + break; + ss.from = (char *) page + ret; + ss.to = (char *) page + ret + 2; + ret += 3; + status = match_hex(&ss, &cmd); + /* either of these cases means invalid input, so do nothing. */ + if (status || cmd >= BLK_SCSI_MAX_CMDS) + return -EINVAL; + + __set_bit(cmd, okbits); + } + + if (rw == READ) + target_okbits = filter->read_ok; + else + target_okbits = filter->write_ok; + + memmove(target_okbits, okbits, sizeof(okbits)); + return count; +} + +static ssize_t rcf_readcmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count) +{ + return rcf_cmds_store(filter, page, count, READ); +} + +static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter, + const char *page, size_t count) +{ + return rcf_cmds_store(filter, page, count, WRITE); +} + +struct rcf_sysfs_entry { + struct attribute attr; + ssize_t (*show)(struct blk_scsi_cmd_filter *, char *); + ssize_t (*store)(struct blk_scsi_cmd_filter *, const char *, size_t); +}; + +static struct rcf_sysfs_entry rcf_readcmds_entry = { + .attr = { .name = "read_table", .mode = S_IRUGO | S_IWUSR }, + .show = rcf_readcmds_show, + .store = rcf_readcmds_store, +}; + +static struct rcf_sysfs_entry rcf_writecmds_entry = { + .attr = {.name = "write_table", .mode = S_IRUGO | S_IWUSR }, + .show = rcf_writecmds_show, + .store = rcf_writecmds_store, +}; + +static struct attribute *default_attrs[] = { + &rcf_readcmds_entry.attr, + &rcf_writecmds_entry.attr, + NULL, +}; + +#define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr) + +static ssize_t +rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page) +{ + struct rcf_sysfs_entry *entry = to_rcf(attr); + struct blk_scsi_cmd_filter *filter; + + filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + if (entry->show) + return entry->show(filter, page); + + return 0; +} + +static ssize_t +rcf_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct rcf_sysfs_entry *entry = to_rcf(attr); + struct blk_scsi_cmd_filter *filter; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (!entry->store) + return -EINVAL; + + filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); + return entry->store(filter, page, length); +} + +static struct sysfs_ops rcf_sysfs_ops = { + .show = rcf_attr_show, + .store = rcf_attr_store, +}; + +static struct kobj_type rcf_ktype = { + .sysfs_ops = &rcf_sysfs_ops, + .default_attrs = default_attrs, +}; + +#ifndef MAINTENANCE_IN_CMD +#define MAINTENANCE_IN_CMD 0xa3 +#endif + +static void rcf_set_defaults(struct blk_scsi_cmd_filter *filter) +{ + /* Basic read-only commands */ + __set_bit(TEST_UNIT_READY, filter->read_ok); + __set_bit(REQUEST_SENSE, filter->read_ok); + __set_bit(READ_6, filter->read_ok); + __set_bit(READ_10, filter->read_ok); + __set_bit(READ_12, filter->read_ok); + __set_bit(READ_16, filter->read_ok); + __set_bit(READ_BUFFER, filter->read_ok); + __set_bit(READ_DEFECT_DATA, filter->read_ok); + __set_bit(READ_CAPACITY, filter->read_ok); + __set_bit(READ_LONG, filter->read_ok); + __set_bit(INQUIRY, filter->read_ok); + __set_bit(MODE_SENSE, filter->read_ok); + __set_bit(MODE_SENSE_10, filter->read_ok); + __set_bit(LOG_SENSE, filter->read_ok); + __set_bit(START_STOP, filter->read_ok); + __set_bit(GPCMD_VERIFY_10, filter->read_ok); + __set_bit(VERIFY_16, filter->read_ok); + __set_bit(REPORT_LUNS, filter->read_ok); + __set_bit(SERVICE_ACTION_IN, filter->read_ok); + __set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok); + __set_bit(MAINTENANCE_IN_CMD, filter->read_ok); + __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); + + /* Audio CD commands */ + __set_bit(GPCMD_PLAY_CD, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); + __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); + __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); + + /* CD/DVD data reading */ + __set_bit(GPCMD_READ_CD, filter->read_ok); + __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); + __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); + __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); + __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); + __set_bit(GPCMD_READ_HEADER, filter->read_ok); + __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); + __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); + __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); + __set_bit(GPCMD_REPORT_KEY, filter->read_ok); + __set_bit(GPCMD_SCAN, filter->read_ok); + __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); + __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); + __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); + __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); + __set_bit(GPCMD_SEEK, filter->read_ok); + __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); + + /* Basic writing commands */ + __set_bit(WRITE_6, filter->write_ok); + __set_bit(WRITE_10, filter->write_ok); + __set_bit(WRITE_VERIFY, filter->write_ok); + __set_bit(WRITE_12, filter->write_ok); + __set_bit(WRITE_VERIFY_12, filter->write_ok); + __set_bit(WRITE_16, filter->write_ok); + __set_bit(WRITE_LONG, filter->write_ok); + __set_bit(WRITE_LONG_2, filter->write_ok); + __set_bit(ERASE, filter->write_ok); + __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); + __set_bit(MODE_SELECT, filter->write_ok); + __set_bit(LOG_SELECT, filter->write_ok); + __set_bit(GPCMD_BLANK, filter->write_ok); + __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); + __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); + __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); + __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); + __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); + __set_bit(GPCMD_SEND_EVENT, filter->write_ok); + __set_bit(GPCMD_SEND_KEY, filter->write_ok); + __set_bit(GPCMD_SEND_OPC, filter->write_ok); + __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); + __set_bit(GPCMD_SET_SPEED, filter->write_ok); + __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); + __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); + __set_bit(GPCMD_SET_STREAMING, filter->write_ok); +} + +int blk_register_filter(struct gendisk *disk) +{ + int ret; + struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + struct kobject *parent = kobject_get(disk->holder_dir->parent); + + if (!parent) + return -ENODEV; + + ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent, + "%s", "cmd_filter"); + + if (ret < 0) + return ret; + + rcf_set_defaults(filter); + return 0; +} + +void blk_unregister_filter(struct gendisk *disk) +{ + struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; + + kobject_put(&filter->kobj); + kobject_put(disk->holder_dir->parent); +} + diff --git a/block/elevator.c b/block/elevator.c index 902dd1344d5..ed6f8f32d27 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -86,6 +86,12 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) if (rq->rq_disk != bio->bi_bdev->bd_disk || rq->special) return 0; + /* + * only merge integrity protected bio into ditto rq + */ + if (bio_integrity(bio) != blk_integrity_rq(rq)) + return 0; + if (!elv_iosched_allow_merge(rq, bio)) return 0; @@ -144,7 +150,7 @@ static struct elevator_type *elevator_get(const char *name) else sprintf(elv, "%s-iosched", name); - request_module(elv); + request_module("%s", elv); spin_lock(&elv_list_lock); e = elevator_find(name); } diff --git a/block/genhd.c b/block/genhd.c index b922d4801c8..9074f384b09 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -189,6 +189,7 @@ void add_disk(struct gendisk *disk) disk->minors, NULL, exact_match, exact_lock, disk); register_disk(disk); blk_register_queue(disk); + blk_register_filter(disk); bdi = &disk->queue->backing_dev_info; bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); @@ -200,6 +201,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ void unlink_gendisk(struct gendisk *disk) { + blk_unregister_filter(disk); sysfs_remove_link(&disk->dev.kobj, "bdi"); bdi_unregister(&disk->queue->backing_dev_info); blk_unregister_queue(disk); @@ -400,6 +402,14 @@ static ssize_t disk_removable_show(struct device *dev, (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); } +static ssize_t disk_ro_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + + return sprintf(buf, "%d\n", disk->policy ? 1 : 0); +} + static ssize_t disk_size_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -472,6 +482,7 @@ static ssize_t disk_fail_store(struct device *dev, static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); +static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); @@ -483,6 +494,7 @@ static struct device_attribute dev_attr_fail = static struct attribute *disk_attrs[] = { &dev_attr_range.attr, &dev_attr_removable.attr, + &dev_attr_ro.attr, &dev_attr_size.attr, &dev_attr_capability.attr, &dev_attr_stat.attr, diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index 78199c08ec9..c5b9bcfc0a6 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -105,120 +105,12 @@ static int sg_emulated_host(struct request_queue *q, int __user *p) return put_user(1, p); } -#define CMD_READ_SAFE 0x01 -#define CMD_WRITE_SAFE 0x02 -#define CMD_WARNED 0x04 -#define safe_for_read(cmd) [cmd] = CMD_READ_SAFE -#define safe_for_write(cmd) [cmd] = CMD_WRITE_SAFE - -int blk_verify_command(unsigned char *cmd, int has_write_perm) -{ - static unsigned char cmd_type[256] = { - - /* Basic read-only commands */ - safe_for_read(TEST_UNIT_READY), - safe_for_read(REQUEST_SENSE), - safe_for_read(READ_6), - safe_for_read(READ_10), - safe_for_read(READ_12), - safe_for_read(READ_16), - safe_for_read(READ_BUFFER), - safe_for_read(READ_DEFECT_DATA), - safe_for_read(READ_LONG), - safe_for_read(INQUIRY), - safe_for_read(MODE_SENSE), - safe_for_read(MODE_SENSE_10), - safe_for_read(LOG_SENSE), - safe_for_read(START_STOP), - safe_for_read(GPCMD_VERIFY_10), - safe_for_read(VERIFY_16), - - /* Audio CD commands */ - safe_for_read(GPCMD_PLAY_CD), - safe_for_read(GPCMD_PLAY_AUDIO_10), - safe_for_read(GPCMD_PLAY_AUDIO_MSF), - safe_for_read(GPCMD_PLAY_AUDIO_TI), - safe_for_read(GPCMD_PAUSE_RESUME), - - /* CD/DVD data reading */ - safe_for_read(GPCMD_READ_BUFFER_CAPACITY), - safe_for_read(GPCMD_READ_CD), - safe_for_read(GPCMD_READ_CD_MSF), - safe_for_read(GPCMD_READ_DISC_INFO), - safe_for_read(GPCMD_READ_CDVD_CAPACITY), - safe_for_read(GPCMD_READ_DVD_STRUCTURE), - safe_for_read(GPCMD_READ_HEADER), - safe_for_read(GPCMD_READ_TRACK_RZONE_INFO), - safe_for_read(GPCMD_READ_SUBCHANNEL), - safe_for_read(GPCMD_READ_TOC_PMA_ATIP), - safe_for_read(GPCMD_REPORT_KEY), - safe_for_read(GPCMD_SCAN), - safe_for_read(GPCMD_GET_CONFIGURATION), - safe_for_read(GPCMD_READ_FORMAT_CAPACITIES), - safe_for_read(GPCMD_GET_EVENT_STATUS_NOTIFICATION), - safe_for_read(GPCMD_GET_PERFORMANCE), - safe_for_read(GPCMD_SEEK), - safe_for_read(GPCMD_STOP_PLAY_SCAN), - - /* Basic writing commands */ - safe_for_write(WRITE_6), - safe_for_write(WRITE_10), - safe_for_write(WRITE_VERIFY), - safe_for_write(WRITE_12), - safe_for_write(WRITE_VERIFY_12), - safe_for_write(WRITE_16), - safe_for_write(WRITE_LONG), - safe_for_write(WRITE_LONG_2), - safe_for_write(ERASE), - safe_for_write(GPCMD_MODE_SELECT_10), - safe_for_write(MODE_SELECT), - safe_for_write(LOG_SELECT), - safe_for_write(GPCMD_BLANK), - safe_for_write(GPCMD_CLOSE_TRACK), - safe_for_write(GPCMD_FLUSH_CACHE), - safe_for_write(GPCMD_FORMAT_UNIT), - safe_for_write(GPCMD_REPAIR_RZONE_TRACK), - safe_for_write(GPCMD_RESERVE_RZONE_TRACK), - safe_for_write(GPCMD_SEND_DVD_STRUCTURE), - safe_for_write(GPCMD_SEND_EVENT), - safe_for_write(GPCMD_SEND_KEY), - safe_for_write(GPCMD_SEND_OPC), - safe_for_write(GPCMD_SEND_CUE_SHEET), - safe_for_write(GPCMD_SET_SPEED), - safe_for_write(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL), - safe_for_write(GPCMD_LOAD_UNLOAD), - safe_for_write(GPCMD_SET_STREAMING), - }; - unsigned char type = cmd_type[cmd[0]]; - - /* Anybody who can open the device can do a read-safe command */ - if (type & CMD_READ_SAFE) - return 0; - - /* Write-safe commands just require a writable open.. */ - if ((type & CMD_WRITE_SAFE) && has_write_perm) - return 0; - - /* And root can do any command.. */ - if (capable(CAP_SYS_RAWIO)) - return 0; - - if (!type) { - cmd_type[cmd[0]] = CMD_WARNED; - printk(KERN_WARNING "scsi: unknown opcode 0x%02x\n", cmd[0]); - } - - /* Otherwise fail it with an "Operation not permitted" */ - return -EPERM; -} -EXPORT_SYMBOL_GPL(blk_verify_command); - static int blk_fill_sghdr_rq(struct request_queue *q, struct request *rq, - struct sg_io_hdr *hdr, int has_write_perm) + struct sg_io_hdr *hdr, struct file *file) { if (copy_from_user(rq->cmd, hdr->cmdp, hdr->cmd_len)) return -EFAULT; - if (blk_verify_command(rq->cmd, has_write_perm)) + if (blk_verify_command(file, rq->cmd)) return -EPERM; /* @@ -287,7 +179,7 @@ static int sg_io(struct file *file, struct request_queue *q, struct gendisk *bd_disk, struct sg_io_hdr *hdr) { unsigned long start_time; - int writing = 0, ret = 0, has_write_perm = 0; + int writing = 0, ret = 0; struct request *rq; char sense[SCSI_SENSE_BUFFERSIZE]; struct bio *bio; @@ -316,10 +208,7 @@ static int sg_io(struct file *file, struct request_queue *q, if (!rq) return -ENOMEM; - if (file) - has_write_perm = file->f_mode & FMODE_WRITE; - - if (blk_fill_sghdr_rq(q, rq, hdr, has_write_perm)) { + if (blk_fill_sghdr_rq(q, rq, hdr, file)) { blk_put_request(rq); return -EFAULT; } @@ -451,7 +340,7 @@ int sg_scsi_ioctl(struct file *file, struct request_queue *q, if (in_len && copy_from_user(buffer, sic->data + cmdlen, in_len)) goto error; - err = blk_verify_command(rq->cmd, file->f_mode & FMODE_WRITE); + err = blk_verify_command(file, rq->cmd); if (err) goto error; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 57a43649a46..499ccc628d8 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -885,7 +885,8 @@ static int ata_scsi_dev_config(struct scsi_device *sdev, /* set the min alignment and padding */ blk_queue_update_dma_alignment(sdev->request_queue, ATA_DMA_PAD_SZ - 1); - blk_queue_dma_pad(sdev->request_queue, ATA_DMA_PAD_SZ - 1); + blk_queue_update_dma_pad(sdev->request_queue, + ATA_DMA_PAD_SZ - 1); /* configure draining */ buf = kmalloc(ATAPI_MAX_DRAIN, q->bounce_gfp | GFP_KERNEL); diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index cd03473f354..a002a381df9 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -6628,15 +6628,18 @@ static void DAC960_DestroyProcEntries(DAC960_Controller_T *Controller) * DAC960_gam_ioctl is the ioctl function for performing RAID operations. */ -static int DAC960_gam_ioctl(struct inode *inode, struct file *file, - unsigned int Request, unsigned long Argument) +static long DAC960_gam_ioctl(struct file *file, unsigned int Request, + unsigned long Argument) { - int ErrorCode = 0; + long ErrorCode = 0; if (!capable(CAP_SYS_ADMIN)) return -EACCES; + + lock_kernel(); switch (Request) { case DAC960_IOCTL_GET_CONTROLLER_COUNT: - return DAC960_ControllerCount; + ErrorCode = DAC960_ControllerCount; + break; case DAC960_IOCTL_GET_CONTROLLER_INFO: { DAC960_ControllerInfo_T __user *UserSpaceControllerInfo = @@ -6644,15 +6647,20 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, DAC960_ControllerInfo_T ControllerInfo; DAC960_Controller_T *Controller; int ControllerNumber; - if (UserSpaceControllerInfo == NULL) return -EINVAL; - ErrorCode = get_user(ControllerNumber, + if (UserSpaceControllerInfo == NULL) + ErrorCode = -EINVAL; + else ErrorCode = get_user(ControllerNumber, &UserSpaceControllerInfo->ControllerNumber); - if (ErrorCode != 0) return ErrorCode; + if (ErrorCode != 0) + break;; + ErrorCode = -ENXIO; if (ControllerNumber < 0 || - ControllerNumber > DAC960_ControllerCount - 1) - return -ENXIO; + ControllerNumber > DAC960_ControllerCount - 1) { + break; + } Controller = DAC960_Controllers[ControllerNumber]; - if (Controller == NULL) return -ENXIO; + if (Controller == NULL) + break;; memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T)); ControllerInfo.ControllerNumber = ControllerNumber; ControllerInfo.FirmwareType = Controller->FirmwareType; @@ -6665,8 +6673,9 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, ControllerInfo.PCI_Address = Controller->PCI_Address; strcpy(ControllerInfo.ModelName, Controller->ModelName); strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion); - return (copy_to_user(UserSpaceControllerInfo, &ControllerInfo, + ErrorCode = (copy_to_user(UserSpaceControllerInfo, &ControllerInfo, sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0); + break; } case DAC960_IOCTL_V1_EXECUTE_COMMAND: { @@ -6684,30 +6693,39 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, int ControllerNumber, DataTransferLength; unsigned char *DataTransferBuffer = NULL; dma_addr_t DataTransferBufferDMA; - if (UserSpaceUserCommand == NULL) return -EINVAL; + if (UserSpaceUserCommand == NULL) { + ErrorCode = -EINVAL; + break; + } if (copy_from_user(&UserCommand, UserSpaceUserCommand, sizeof(DAC960_V1_UserCommand_T))) { ErrorCode = -EFAULT; - goto Failure1a; + break; } ControllerNumber = UserCommand.ControllerNumber; + ErrorCode = -ENXIO; if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) - return -ENXIO; + break; Controller = DAC960_Controllers[ControllerNumber]; - if (Controller == NULL) return -ENXIO; - if (Controller->FirmwareType != DAC960_V1_Controller) return -EINVAL; + if (Controller == NULL) + break; + ErrorCode = -EINVAL; + if (Controller->FirmwareType != DAC960_V1_Controller) + break; CommandOpcode = UserCommand.CommandMailbox.Common.CommandOpcode; DataTransferLength = UserCommand.DataTransferLength; - if (CommandOpcode & 0x80) return -EINVAL; + if (CommandOpcode & 0x80) + break; if (CommandOpcode == DAC960_V1_DCDB) { if (copy_from_user(&DCDB, UserCommand.DCDB, sizeof(DAC960_V1_DCDB_T))) { ErrorCode = -EFAULT; - goto Failure1a; + break; } - if (DCDB.Channel >= DAC960_V1_MaxChannels) return -EINVAL; + if (DCDB.Channel >= DAC960_V1_MaxChannels) + break; if (!((DataTransferLength == 0 && DCDB.Direction == DAC960_V1_DCDB_NoDataTransfer) || @@ -6717,38 +6735,37 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, (DataTransferLength < 0 && DCDB.Direction == DAC960_V1_DCDB_DataTransferSystemToDevice))) - return -EINVAL; + break; if (((DCDB.TransferLengthHigh4 << 16) | DCDB.TransferLength) != abs(DataTransferLength)) - return -EINVAL; + break; DCDB_IOBUF = pci_alloc_consistent(Controller->PCIDevice, sizeof(DAC960_V1_DCDB_T), &DCDB_IOBUFDMA); - if (DCDB_IOBUF == NULL) - return -ENOMEM; + if (DCDB_IOBUF == NULL) { + ErrorCode = -ENOMEM; + break; + } } + ErrorCode = -ENOMEM; if (DataTransferLength > 0) { DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice, DataTransferLength, &DataTransferBufferDMA); - if (DataTransferBuffer == NULL) { - ErrorCode = -ENOMEM; - goto Failure1; - } + if (DataTransferBuffer == NULL) + break; memset(DataTransferBuffer, 0, DataTransferLength); } else if (DataTransferLength < 0) { DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice, -DataTransferLength, &DataTransferBufferDMA); - if (DataTransferBuffer == NULL) { - ErrorCode = -ENOMEM; - goto Failure1; - } + if (DataTransferBuffer == NULL) + break; if (copy_from_user(DataTransferBuffer, UserCommand.DataTransferBuffer, -DataTransferLength)) { ErrorCode = -EFAULT; - goto Failure1; + break; } } if (CommandOpcode == DAC960_V1_DCDB) @@ -6825,8 +6842,7 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, if (DCDB_IOBUF != NULL) pci_free_consistent(Controller->PCIDevice, sizeof(DAC960_V1_DCDB_T), DCDB_IOBUF, DCDB_IOBUFDMA); - Failure1a: - return ErrorCode; + break; } case DAC960_IOCTL_V2_EXECUTE_COMMAND: { @@ -6844,32 +6860,43 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, dma_addr_t DataTransferBufferDMA; unsigned char *RequestSenseBuffer = NULL; dma_addr_t RequestSenseBufferDMA; - if (UserSpaceUserCommand == NULL) return -EINVAL; + + ErrorCode = -EINVAL; + if (UserSpaceUserCommand == NULL) + break; if (copy_from_user(&UserCommand, UserSpaceUserCommand, sizeof(DAC960_V2_UserCommand_T))) { ErrorCode = -EFAULT; - goto Failure2a; + break; } + ErrorCode = -ENXIO; ControllerNumber = UserCommand.ControllerNumber; if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) - return -ENXIO; + break; Controller = DAC960_Controllers[ControllerNumber]; - if (Controller == NULL) return -ENXIO; - if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL; + if (Controller == NULL) + break; + if (Controller->FirmwareType != DAC960_V2_Controller){ + ErrorCode = -EINVAL; + break; + } DataTransferLength = UserCommand.DataTransferLength; + ErrorCode = -ENOMEM; if (DataTransferLength > 0) { DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice, DataTransferLength, &DataTransferBufferDMA); - if (DataTransferBuffer == NULL) return -ENOMEM; + if (DataTransferBuffer == NULL) + break; memset(DataTransferBuffer, 0, DataTransferLength); } else if (DataTransferLength < 0) { DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice, -DataTransferLength, &DataTransferBufferDMA); - if (DataTransferBuffer == NULL) return -ENOMEM; + if (DataTransferBuffer == NULL) + break; if (copy_from_user(DataTransferBuffer, UserCommand.DataTransferBuffer, -DataTransferLength)) { @@ -6979,8 +7006,7 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, if (RequestSenseBuffer != NULL) pci_free_consistent(Controller->PCIDevice, RequestSenseLength, RequestSenseBuffer, RequestSenseBufferDMA); - Failure2a: - return ErrorCode; + break; } case DAC960_IOCTL_V2_GET_HEALTH_STATUS: { @@ -6990,21 +7016,33 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, DAC960_V2_HealthStatusBuffer_T HealthStatusBuffer; DAC960_Controller_T *Controller; int ControllerNumber; - if (UserSpaceGetHealthStatus == NULL) return -EINVAL; + if (UserSpaceGetHealthStatus == NULL) { + ErrorCode = -EINVAL; + break; + } if (copy_from_user(&GetHealthStatus, UserSpaceGetHealthStatus, - sizeof(DAC960_V2_GetHealthStatus_T))) - return -EFAULT; + sizeof(DAC960_V2_GetHealthStatus_T))) { + ErrorCode = -EFAULT; + break; + } + ErrorCode = -ENXIO; ControllerNumber = GetHealthStatus.ControllerNumber; if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) - return -ENXIO; + break; Controller = DAC960_Controllers[ControllerNumber]; - if (Controller == NULL) return -ENXIO; - if (Controller->FirmwareType != DAC960_V2_Controller) return -EINVAL; + if (Controller == NULL) + break; + if (Controller->FirmwareType != DAC960_V2_Controller) { + ErrorCode = -EINVAL; + break; + } if (copy_from_user(&HealthStatusBuffer, GetHealthStatus.HealthStatusBuffer, - sizeof(DAC960_V2_HealthStatusBuffer_T))) - return -EFAULT; + sizeof(DAC960_V2_HealthStatusBuffer_T))) { + ErrorCode = -EFAULT; + break; + } while (Controller->V2.HealthStatusBuffer->StatusChangeCounter == HealthStatusBuffer.StatusChangeCounter && Controller->V2.HealthStatusBuffer->NextEventSequenceNumber @@ -7012,21 +7050,28 @@ static int DAC960_gam_ioctl(struct inode *inode, struct file *file, { interruptible_sleep_on_timeout(&Controller->HealthStatusWaitQueue, DAC960_MonitoringTimerInterval); - if (signal_pending(current)) return -EINTR; + if (signal_pending(current)) { + ErrorCode = -EINTR; + break; + } } if (copy_to_user(GetHealthStatus.HealthStatusBuffer, Controller->V2.HealthStatusBuffer, sizeof(DAC960_V2_HealthStatusBuffer_T))) - return -EFAULT; - return 0; + ErrorCode = -EFAULT; + else + ErrorCode = 0; } + default: + ErrorCode = -ENOTTY; } - return -EINVAL; + unlock_kernel(); + return ErrorCode; } static const struct file_operations DAC960_gam_fops = { .owner = THIS_MODULE, - .ioctl = DAC960_gam_ioctl + .unlocked_ioctl = DAC960_gam_ioctl }; static struct miscdevice DAC960_gam_dev = { diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 41f818be2f7..2f1746295d0 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -1003,7 +1003,7 @@ aoecmd_cfg_rsp(struct sk_buff *skb) * Enough people have their dip switches set backwards to * warrant a loud message for this special case. */ - aoemajor = be16_to_cpu(get_unaligned(&h->major)); + aoemajor = get_unaligned_be16(&h->major); if (aoemajor == 0xfff) { printk(KERN_ERR "aoe: Warning: shelf address is all ones. " "Check shelf dip switches.\n"); diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 8b9549ab4a4..27455ee1e9d 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -146,6 +146,7 @@ static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3}; #include <linux/mtio.h> #include <linux/device.h> #include <linux/sched.h> /* current, TASK_*, schedule_timeout() */ +#include <linux/smp_lock.h> #include <asm/uaccess.h> @@ -189,8 +190,7 @@ module_param_array(drive3, int, NULL, 0); #define ATAPI_LOG_SENSE 0x4d static int pt_open(struct inode *inode, struct file *file); -static int pt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static int pt_release(struct inode *inode, struct file *file); static ssize_t pt_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos); @@ -236,7 +236,7 @@ static const struct file_operations pt_fops = { .owner = THIS_MODULE, .read = pt_read, .write = pt_write, - .ioctl = pt_ioctl, + .unlocked_ioctl = pt_ioctl, .open = pt_open, .release = pt_release, }; @@ -685,8 +685,7 @@ out: return err; } -static int pt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long pt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct pt_unit *tape = file->private_data; struct mtop __user *p = (void __user *)arg; @@ -700,23 +699,26 @@ static int pt_ioctl(struct inode *inode, struct file *file, switch (mtop.mt_op) { case MTREW: + lock_kernel(); pt_rewind(tape); + unlock_kernel(); return 0; case MTWEOF: + lock_kernel(); pt_write_fm(tape); + unlock_kernel(); return 0; default: - printk("%s: Unimplemented mt_op %d\n", tape->name, + /* FIXME: rate limit ?? */ + printk(KERN_DEBUG "%s: Unimplemented mt_op %d\n", tape->name, mtop.mt_op); return -EINVAL; } default: - printk("%s: Unimplemented ioctl 0x%x\n", tape->name, cmd); - return -EINVAL; - + return -ENOTTY; } } diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 3ba1df93e9e..45bee918c46 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -49,6 +49,7 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/spinlock.h> #include <linux/file.h> @@ -2079,7 +2080,6 @@ static noinline_for_stack int pkt_write_caching(struct pktcdvd_device *pd, unsigned char buf[64]; int ret; - memset(buf, 0, sizeof(buf)); init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.sense = &sense; cgc.buflen = pd->mode_offset + 12; @@ -2126,7 +2126,6 @@ static noinline_for_stack int pkt_get_max_speed(struct pktcdvd_device *pd, unsigned char *cap_buf; int ret, offset; - memset(buf, 0, sizeof(buf)); cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset]; init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); cgc.sense = &sense; @@ -2633,11 +2632,12 @@ end_io: -static int pkt_merge_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *bvec) +static int pkt_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd, + struct bio_vec *bvec) { struct pktcdvd_device *pd = q->queuedata; - sector_t zone = ZONE(bio->bi_sector, pd); - int used = ((bio->bi_sector - zone) << 9) + bio->bi_size; + sector_t zone = ZONE(bmd->bi_sector, pd); + int used = ((bmd->bi_sector - zone) << 9) + bmd->bi_size; int remaining = (pd->settings.size << 9) - used; int remaining2; @@ -2645,7 +2645,7 @@ static int pkt_merge_bvec(struct request_queue *q, struct bio *bio, struct bio_v * A bio <= PAGE_SIZE must be allowed. If it crosses a packet * boundary, pkt_make_request() will split the bio. */ - remaining2 = PAGE_SIZE - bio->bi_size; + remaining2 = PAGE_SIZE - bmd->bi_size; remaining = max(remaining, remaining2); BUG_ON(remaining < 0); @@ -2796,9 +2796,14 @@ out_mem: return ret; } -static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long pkt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct pktcdvd_device *pd = inode->i_bdev->bd_disk->private_data; + struct inode *inode = file->f_path.dentry->d_inode; + struct pktcdvd_device *pd; + long ret; + + lock_kernel(); + pd = inode->i_bdev->bd_disk->private_data; VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode)); @@ -2811,7 +2816,8 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u case CDROM_LAST_WRITTEN: case CDROM_SEND_PACKET: case SCSI_IOCTL_SEND_COMMAND: - return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); + ret = blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); + break; case CDROMEJECT: /* @@ -2820,14 +2826,15 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u */ if (pd->refcnt == 1) pkt_lock_door(pd, 0); - return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); + ret = blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg); + break; default: VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd); - return -ENOTTY; + ret = -ENOTTY; } - - return 0; + unlock_kernel(); + return ret; } static int pkt_media_changed(struct gendisk *disk) @@ -2849,7 +2856,7 @@ static struct block_device_operations pktcdvd_ops = { .owner = THIS_MODULE, .open = pkt_open, .release = pkt_close, - .ioctl = pkt_ioctl, + .unlocked_ioctl = pkt_ioctl, .media_changed = pkt_media_changed, }; @@ -3014,7 +3021,8 @@ static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd) mutex_unlock(&ctl_mutex); } -static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long pkt_ctl_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { void __user *argp = (void __user *)arg; struct pkt_ctrl_command ctrl_cmd; @@ -3031,16 +3039,22 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm case PKT_CTRL_CMD_SETUP: if (!capable(CAP_SYS_ADMIN)) return -EPERM; + lock_kernel(); ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev); ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev); + unlock_kernel(); break; case PKT_CTRL_CMD_TEARDOWN: if (!capable(CAP_SYS_ADMIN)) return -EPERM; + lock_kernel(); ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev)); + unlock_kernel(); break; case PKT_CTRL_CMD_STATUS: + lock_kernel(); pkt_get_status(&ctrl_cmd); + unlock_kernel(); break; default: return -ENOTTY; @@ -3053,7 +3067,7 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm static const struct file_operations pkt_ctl_fops = { - .ioctl = pkt_ctl_ioctl, + .unlocked_ioctl = pkt_ctl_ioctl, .owner = THIS_MODULE, }; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index f2fff5799dd..9ae05c58423 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -38,6 +38,7 @@ #include <linux/interrupt.h> #include <linux/blkdev.h> #include <linux/hdreg.h> +#include <linux/cdrom.h> #include <linux/module.h> #include <xen/xenbus.h> @@ -153,6 +154,40 @@ static int blkif_getgeo(struct block_device *bd, struct hd_geometry *hg) return 0; } +int blkif_ioctl(struct inode *inode, struct file *filep, + unsigned command, unsigned long argument) +{ + struct blkfront_info *info = + inode->i_bdev->bd_disk->private_data; + int i; + + dev_dbg(&info->xbdev->dev, "command: 0x%x, argument: 0x%lx\n", + command, (long)argument); + + switch (command) { + case CDROMMULTISESSION: + dev_dbg(&info->xbdev->dev, "FIXME: support multisession CDs later\n"); + for (i = 0; i < sizeof(struct cdrom_multisession); i++) + if (put_user(0, (char __user *)(argument + i))) + return -EFAULT; + return 0; + + case CDROM_GET_CAPABILITY: { + struct gendisk *gd = info->gd; + if (gd->flags & GENHD_FL_CD) + return 0; + return -EINVAL; + } + + default: + /*printk(KERN_ALERT "ioctl %08x not supported by Xen blkdev\n", + command);*/ + return -EINVAL; /* same return as native Linux */ + } + + return 0; +} + /* * blkif_queue_request * @@ -324,6 +359,9 @@ static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) /* Make sure buffer addresses are sector-aligned. */ blk_queue_dma_alignment(rq, 511); + /* Make sure we don't use bounce buffers. */ + blk_queue_bounce_limit(rq, BLK_BOUNCE_ANY); + gd->queue = rq; return 0; @@ -546,7 +584,7 @@ static int setup_blkring(struct xenbus_device *dev, info->ring_ref = GRANT_INVALID_REF; - sring = (struct blkif_sring *)__get_free_page(GFP_KERNEL); + sring = (struct blkif_sring *)__get_free_page(GFP_NOIO | __GFP_HIGH); if (!sring) { xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); return -ENOMEM; @@ -703,7 +741,8 @@ static int blkif_recover(struct blkfront_info *info) int j; /* Stage 1: Make a safe copy of the shadow state. */ - copy = kmalloc(sizeof(info->shadow), GFP_KERNEL); + copy = kmalloc(sizeof(info->shadow), + GFP_NOIO | __GFP_REPEAT | __GFP_HIGH); if (!copy) return -ENOMEM; memcpy(copy, info->shadow, sizeof(info->shadow)); @@ -959,7 +998,7 @@ static int blkif_release(struct inode *inode, struct file *filep) struct xenbus_device *dev = info->xbdev; enum xenbus_state state = xenbus_read_driver_state(dev->otherend); - if (state == XenbusStateClosing) + if (state == XenbusStateClosing && info->is_ready) blkfront_closing(dev); } return 0; @@ -971,6 +1010,7 @@ static struct block_device_operations xlvbd_block_fops = .open = blkif_open, .release = blkif_release, .getgeo = blkif_getgeo, + .ioctl = blkif_ioctl, }; @@ -1006,7 +1046,7 @@ static int __init xlblk_init(void) module_init(xlblk_init); -static void xlblk_exit(void) +static void __exit xlblk_exit(void) { return xenbus_unregister_driver(&blkfront); } diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 69f26eb6415..a5da3563265 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -461,37 +461,27 @@ int cdrom_get_media_event(struct cdrom_device_info *cdi, struct media_event_desc *med) { struct packet_command cgc; - unsigned char *buffer; - struct event_header *eh; - int ret = 1; - - buffer = kmalloc(8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + unsigned char buffer[8]; + struct event_header *eh = (struct event_header *) buffer; - eh = (struct event_header *)buffer; - - init_cdrom_command(&cgc, buffer, 8, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION; cgc.cmd[1] = 1; /* IMMED */ cgc.cmd[4] = 1 << 4; /* media event */ - cgc.cmd[8] = 8; + cgc.cmd[8] = sizeof(buffer); cgc.quiet = 1; if (cdi->ops->generic_packet(cdi, &cgc)) - goto err; + return 1; if (be16_to_cpu(eh->data_len) < sizeof(*med)) - goto err; + return 1; if (eh->nea || eh->notification_class != 0x4) - goto err; + return 1; - memcpy(med, buffer + sizeof(*eh), sizeof(*med)); - ret = 0; -err: - kfree(buffer); - return ret; + memcpy(med, &buffer[sizeof(*eh)], sizeof(*med)); + return 0; } /* @@ -501,82 +491,68 @@ err: static int cdrom_mrw_probe_pc(struct cdrom_device_info *cdi) { struct packet_command cgc; - char *buffer; - int ret = 1; - - buffer = kmalloc(16, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + char buffer[16]; - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.timeout = HZ; cgc.quiet = 1; if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC, 0)) { cdi->mrw_mode_page = MRW_MODE_PC; - ret = 0; + return 0; } else if (!cdrom_mode_sense(cdi, &cgc, MRW_MODE_PC_PRE1, 0)) { cdi->mrw_mode_page = MRW_MODE_PC_PRE1; - ret = 0; + return 0; } - kfree(buffer); - return ret; + + return 1; } static int cdrom_is_mrw(struct cdrom_device_info *cdi, int *write) { struct packet_command cgc; struct mrw_feature_desc *mfd; - unsigned char *buffer; + unsigned char buffer[16]; int ret; *write = 0; - buffer = kmalloc(16, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_CONFIGURATION; cgc.cmd[3] = CDF_MRW; - cgc.cmd[8] = 16; + cgc.cmd[8] = sizeof(buffer); cgc.quiet = 1; if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - goto err; + return ret; mfd = (struct mrw_feature_desc *)&buffer[sizeof(struct feature_header)]; - if (be16_to_cpu(mfd->feature_code) != CDF_MRW) { - ret = 1; - goto err; - } + if (be16_to_cpu(mfd->feature_code) != CDF_MRW) + return 1; *write = mfd->write; if ((ret = cdrom_mrw_probe_pc(cdi))) { *write = 0; + return ret; } -err: - kfree(buffer); - return ret; + + return 0; } static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) { struct packet_command cgc; - unsigned char *buffer; + unsigned char buffer[12]; int ret; printk(KERN_INFO "cdrom: %sstarting format\n", cont ? "Re" : ""); - buffer = kmalloc(12, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - /* * FmtData bit set (bit 4), format type is 1 */ - init_cdrom_command(&cgc, buffer, 12, CGC_DATA_WRITE); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_WRITE); cgc.cmd[0] = GPCMD_FORMAT_UNIT; cgc.cmd[1] = (1 << 4) | 1; @@ -603,7 +579,6 @@ static int cdrom_mrw_bgformat(struct cdrom_device_info *cdi, int cont) if (ret) printk(KERN_INFO "cdrom: bgformat failed\n"); - kfree(buffer); return ret; } @@ -663,17 +638,16 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space) { struct packet_command cgc; struct mode_page_header *mph; - char *buffer; + char buffer[16]; int ret, offset, size; - buffer = kmalloc(16, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); + cgc.buffer = buffer; + cgc.buflen = sizeof(buffer); if ((ret = cdrom_mode_sense(cdi, &cgc, cdi->mrw_mode_page, 0))) - goto err; + return ret; mph = (struct mode_page_header *) buffer; offset = be16_to_cpu(mph->desc_length); @@ -683,70 +657,55 @@ static int cdrom_mrw_set_lba_space(struct cdrom_device_info *cdi, int space) cgc.buflen = size; if ((ret = cdrom_mode_select(cdi, &cgc))) - goto err; + return ret; printk(KERN_INFO "cdrom: %s: mrw address space %s selected\n", cdi->name, mrw_address_space[space]); - ret = 0; -err: - kfree(buffer); - return ret; + return 0; } static int cdrom_get_random_writable(struct cdrom_device_info *cdi, struct rwrt_feature_desc *rfd) { struct packet_command cgc; - char *buffer; + char buffer[24]; int ret; - buffer = kmalloc(24, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - init_cdrom_command(&cgc, buffer, 24, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_CONFIGURATION; /* often 0x46 */ cgc.cmd[3] = CDF_RWRT; /* often 0x0020 */ - cgc.cmd[8] = 24; /* often 0x18 */ + cgc.cmd[8] = sizeof(buffer); /* often 0x18 */ cgc.quiet = 1; if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - goto err; + return ret; memcpy(rfd, &buffer[sizeof(struct feature_header)], sizeof (*rfd)); - ret = 0; -err: - kfree(buffer); - return ret; + return 0; } static int cdrom_has_defect_mgt(struct cdrom_device_info *cdi) { struct packet_command cgc; - char *buffer; + char buffer[16]; __be16 *feature_code; int ret; - buffer = kmalloc(16, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_CONFIGURATION; cgc.cmd[3] = CDF_HWDM; - cgc.cmd[8] = 16; + cgc.cmd[8] = sizeof(buffer); cgc.quiet = 1; if ((ret = cdi->ops->generic_packet(cdi, &cgc))) - goto err; + return ret; feature_code = (__be16 *) &buffer[sizeof(struct feature_header)]; if (be16_to_cpu(*feature_code) == CDF_HWDM) - ret = 0; -err: - kfree(buffer); - return ret; + return 0; + + return 1; } @@ -837,14 +796,10 @@ static int cdrom_mrw_open_write(struct cdrom_device_info *cdi) static int mo_open_write(struct cdrom_device_info *cdi) { struct packet_command cgc; - char *buffer; + char buffer[255]; int ret; - buffer = kmalloc(255, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - init_cdrom_command(&cgc, buffer, 4, CGC_DATA_READ); + init_cdrom_command(&cgc, &buffer, 4, CGC_DATA_READ); cgc.quiet = 1; /* @@ -861,15 +816,10 @@ static int mo_open_write(struct cdrom_device_info *cdi) } /* drive gave us no info, let the user go ahead */ - if (ret) { - ret = 0; - goto err; - } + if (ret) + return 0; - ret = buffer[3] & 0x80; -err: - kfree(buffer); - return ret; + return buffer[3] & 0x80; } static int cdrom_ram_open_write(struct cdrom_device_info *cdi) @@ -892,19 +842,15 @@ static int cdrom_ram_open_write(struct cdrom_device_info *cdi) static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) { struct packet_command cgc; - char *buffer; + char buffer[32]; int ret, mmc3_profile; - buffer = kmalloc(32, GFP_KERNEL); - if (!buffer) - return; - - init_cdrom_command(&cgc, buffer, 32, CGC_DATA_READ); + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_READ); cgc.cmd[0] = GPCMD_GET_CONFIGURATION; cgc.cmd[1] = 0; cgc.cmd[2] = cgc.cmd[3] = 0; /* Starting Feature Number */ - cgc.cmd[8] = 32; /* Allocation Length */ + cgc.cmd[8] = sizeof(buffer); /* Allocation Length */ cgc.quiet = 1; if ((ret = cdi->ops->generic_packet(cdi, &cgc))) @@ -913,7 +859,6 @@ static void cdrom_mmc3_profile(struct cdrom_device_info *cdi) mmc3_profile = (buffer[6] << 8) | buffer[7]; cdi->mmc3_profile = mmc3_profile; - kfree(buffer); } static int cdrom_is_dvd_rw(struct cdrom_device_info *cdi) @@ -1628,15 +1573,12 @@ static void setup_send_key(struct packet_command *cgc, unsigned agid, unsigned t static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) { int ret; - u_char *buf; + u_char buf[20]; struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; - rpc_state_t *rpc_state; - - buf = kzalloc(20, GFP_KERNEL); - if (!buf) - return -ENOMEM; + rpc_state_t rpc_state; + memset(buf, 0, sizeof(buf)); init_cdrom_command(&cgc, buf, 0, CGC_DATA_READ); switch (ai->type) { @@ -1647,7 +1589,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) setup_report_key(&cgc, ai->lsa.agid, 0); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; ai->lsa.agid = buf[7] >> 6; /* Returning data, let host change state */ @@ -1658,7 +1600,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) setup_report_key(&cgc, ai->lsk.agid, 2); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; copy_key(ai->lsk.key, &buf[4]); /* Returning data, let host change state */ @@ -1669,7 +1611,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) setup_report_key(&cgc, ai->lsc.agid, 1); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; copy_chal(ai->lsc.chal, &buf[4]); /* Returning data, let host change state */ @@ -1686,7 +1628,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) cgc.cmd[2] = ai->lstk.lba >> 24; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; ai->lstk.cpm = (buf[4] >> 7) & 1; ai->lstk.cp_sec = (buf[4] >> 6) & 1; @@ -1700,7 +1642,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) setup_report_key(&cgc, ai->lsasf.agid, 5); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; ai->lsasf.asf = buf[7] & 1; break; @@ -1713,7 +1655,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) copy_chal(&buf[4], ai->hsc.chal); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; ai->type = DVD_LU_SEND_KEY1; break; @@ -1726,7 +1668,7 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) if ((ret = cdo->generic_packet(cdi, &cgc))) { ai->type = DVD_AUTH_FAILURE; - goto err; + return ret; } ai->type = DVD_AUTH_ESTABLISHED; break; @@ -1737,23 +1679,24 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) cdinfo(CD_DVD, "entering DVD_INVALIDATE_AGID\n"); setup_report_key(&cgc, ai->lsa.agid, 0x3f); if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; break; /* Get region settings */ case DVD_LU_SEND_RPC_STATE: cdinfo(CD_DVD, "entering DVD_LU_SEND_RPC_STATE\n"); setup_report_key(&cgc, 0, 8); + memset(&rpc_state, 0, sizeof(rpc_state_t)); + cgc.buffer = (char *) &rpc_state; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; - rpc_state = (rpc_state_t *)buf; - ai->lrpcs.type = rpc_state->type_code; - ai->lrpcs.vra = rpc_state->vra; - ai->lrpcs.ucca = rpc_state->ucca; - ai->lrpcs.region_mask = rpc_state->region_mask; - ai->lrpcs.rpc_scheme = rpc_state->rpc_scheme; + ai->lrpcs.type = rpc_state.type_code; + ai->lrpcs.vra = rpc_state.vra; + ai->lrpcs.ucca = rpc_state.ucca; + ai->lrpcs.region_mask = rpc_state.region_mask; + ai->lrpcs.rpc_scheme = rpc_state.rpc_scheme; break; /* Set region settings */ @@ -1764,23 +1707,20 @@ static int dvd_do_auth(struct cdrom_device_info *cdi, dvd_authinfo *ai) buf[4] = ai->hrpcs.pdrc; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; break; default: cdinfo(CD_WARNING, "Invalid DVD key ioctl (%d)\n", ai->type); - ret = -ENOTTY; - goto err; + return -ENOTTY; } - ret = 0; -err: - kfree(buf); - return ret; + + return 0; } static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) { - unsigned char *buf, *base; + unsigned char buf[21], *base; struct dvd_layer *layer; struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; @@ -1789,11 +1729,7 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) if (layer_num >= DVD_LAYERS) return -EINVAL; - buf = kmalloc(21, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(&cgc, buf, 21, CGC_DATA_READ); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; cgc.cmd[6] = layer_num; cgc.cmd[7] = s->type; @@ -1805,7 +1741,7 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) cgc.quiet = 1; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; base = &buf[4]; layer = &s->physical.layer[layer_num]; @@ -1829,24 +1765,17 @@ static int dvd_read_physical(struct cdrom_device_info *cdi, dvd_struct *s) layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15]; layer->bca = base[16] >> 7; - ret = 0; -err: - kfree(buf); - return ret; + return 0; } static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) { int ret; - u_char *buf; + u_char buf[8]; struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; - buf = kmalloc(8, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(&cgc, buf, 8, CGC_DATA_READ); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; cgc.cmd[6] = s->copyright.layer_num; cgc.cmd[7] = s->type; @@ -1854,15 +1783,12 @@ static int dvd_read_copyright(struct cdrom_device_info *cdi, dvd_struct *s) cgc.cmd[9] = cgc.buflen & 0xff; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; s->copyright.cpst = buf[4]; s->copyright.rmi = buf[5]; - ret = 0; -err: - kfree(buf); - return ret; + return 0; } static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s) @@ -1894,33 +1820,26 @@ static int dvd_read_disckey(struct cdrom_device_info *cdi, dvd_struct *s) static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s) { int ret; - u_char *buf; + u_char buf[4 + 188]; struct packet_command cgc; struct cdrom_device_ops *cdo = cdi->ops; - buf = kmalloc(4 + 188, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - init_cdrom_command(&cgc, buf, 4 + 188, CGC_DATA_READ); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; cgc.cmd[7] = s->type; cgc.cmd[9] = cgc.buflen & 0xff; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; s->bca.len = buf[0] << 8 | buf[1]; if (s->bca.len < 12 || s->bca.len > 188) { cdinfo(CD_WARNING, "Received invalid BCA length (%d)\n", s->bca.len); - ret = -EIO; - goto err; + return -EIO; } memcpy(s->bca.value, &buf[4], s->bca.len); - ret = 0; -err: - kfree(buf); - return ret; + + return 0; } static int dvd_read_manufact(struct cdrom_device_info *cdi, dvd_struct *s) @@ -2020,13 +1939,9 @@ static int cdrom_read_subchannel(struct cdrom_device_info *cdi, { struct cdrom_device_ops *cdo = cdi->ops; struct packet_command cgc; - char *buffer; + char buffer[32]; int ret; - buffer = kmalloc(32, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - init_cdrom_command(&cgc, buffer, 16, CGC_DATA_READ); cgc.cmd[0] = GPCMD_READ_SUBCHANNEL; cgc.cmd[1] = 2; /* MSF addressing */ @@ -2035,7 +1950,7 @@ static int cdrom_read_subchannel(struct cdrom_device_info *cdi, cgc.cmd[8] = 16; if ((ret = cdo->generic_packet(cdi, &cgc))) - goto err; + return ret; subchnl->cdsc_audiostatus = cgc.buffer[1]; subchnl->cdsc_format = CDROM_MSF; @@ -2050,10 +1965,7 @@ static int cdrom_read_subchannel(struct cdrom_device_info *cdi, subchnl->cdsc_absaddr.msf.second = cgc.buffer[10]; subchnl->cdsc_absaddr.msf.frame = cgc.buffer[11]; - ret = 0; -err: - kfree(buffer); - return ret; + return 0; } /* diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 4a933d41342..59ca35156d8 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -32,8 +32,9 @@ #include <linux/fs.h> #include <linux/delay.h> #include <linux/bitrev.h> -#include <asm/uaccess.h> -#include <asm/io.h> +#include <linux/smp_lock.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> @@ -1405,11 +1406,11 @@ static void stop_monitor(struct cm4000_dev *dev) DEBUGP(3, dev, "<- stop_monitor\n"); } -static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) +static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct cm4000_dev *dev = filp->private_data; unsigned int iobase = dev->p_dev->io.BasePort1; + struct inode *inode = filp->f_path.dentry->d_inode; struct pcmcia_device *link; int size; int rc; @@ -1426,38 +1427,42 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode), iminor(inode), ioctl_names[_IOC_NR(cmd)]); + lock_kernel(); + rc = -ENODEV; link = dev_table[iminor(inode)]; if (!pcmcia_dev_present(link)) { DEBUGP(4, dev, "DEV_OK false\n"); - return -ENODEV; + goto out; } if (test_bit(IS_CMM_ABSENT, &dev->flags)) { DEBUGP(4, dev, "CMM_ABSENT flag set\n"); - return -ENODEV; + goto out; } + rc = EINVAL; if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) { DEBUGP(4, dev, "ioctype mismatch\n"); - return -EINVAL; + goto out; } if (_IOC_NR(cmd) > CM_IOC_MAXNR) { DEBUGP(4, dev, "iocnr mismatch\n"); - return -EINVAL; + goto out; } size = _IOC_SIZE(cmd); - rc = 0; + rc = -EFAULT; DEBUGP(4, dev, "iocdir=%.4x iocr=%.4x iocw=%.4x iocsize=%d cmd=%.4x\n", _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd); if (_IOC_DIR(cmd) & _IOC_READ) { if (!access_ok(VERIFY_WRITE, argp, size)) - return -EFAULT; + goto out; } if (_IOC_DIR(cmd) & _IOC_WRITE) { if (!access_ok(VERIFY_READ, argp, size)) - return -EFAULT; + goto out; } + rc = 0; switch (cmd) { case CM_IOCGSTATUS: @@ -1477,9 +1482,9 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, if (test_bit(IS_BAD_CARD, &dev->flags)) status |= CM_BAD_CARD; if (copy_to_user(argp, &status, sizeof(int))) - return -EFAULT; + rc = -EFAULT; } - return 0; + break; case CM_IOCGATR: DEBUGP(4, dev, "... in CM_IOCGATR\n"); { @@ -1492,25 +1497,29 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) { if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; + rc = -EAGAIN; + else + rc = -ERESTARTSYS; + break; } + rc = -EFAULT; if (test_bit(IS_ATR_VALID, &dev->flags) == 0) { tmp = -1; if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int))) - return -EFAULT; + break; } else { if (copy_to_user(atreq->atr, dev->atr, dev->atr_len)) - return -EFAULT; + break; tmp = dev->atr_len; if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int))) - return -EFAULT; + break; } - return 0; + rc = 0; + break; } case CM_IOCARDOFF: @@ -1538,8 +1547,10 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) { if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; + rc = -EAGAIN; + else + rc = -ERESTARTSYS; + break; } /* Set Flags0 = 0x42 */ DEBUGP(4, dev, "Set Flags0=0x42 \n"); @@ -1554,8 +1565,10 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, || (test_bit(IS_ATR_VALID, (void *)&dev->flags) != 0)))) { if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; + rc = -EAGAIN; + else + rc = -ERESTARTSYS; + break; } } /* release lock */ @@ -1568,8 +1581,10 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, struct ptsreq krnptsreq; if (copy_from_user(&krnptsreq, argp, - sizeof(struct ptsreq))) - return -EFAULT; + sizeof(struct ptsreq))) { + rc = -EFAULT; + break; + } rc = 0; DEBUGP(4, dev, "... in CM_IOCSPTS\n"); @@ -1580,8 +1595,10 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) { if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; + rc = -EAGAIN; + else + rc = -ERESTARTSYS; + break; } /* get IO lock */ if (wait_event_interruptible @@ -1590,8 +1607,10 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) { if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - return -ERESTARTSYS; + rc = -EAGAIN; + else + rc = -ERESTARTSYS; + break; } if ((rc = set_protocol(dev, &krnptsreq)) != 0) { @@ -1604,7 +1623,7 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, wake_up_interruptible(&dev->ioq); } - return rc; + break; #ifdef PCMCIA_DEBUG case CM_IOSDBGLVL: /* set debug log level */ { @@ -1612,18 +1631,20 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, old_pc_debug = pc_debug; if (copy_from_user(&pc_debug, argp, sizeof(int))) - return -EFAULT; - - if (old_pc_debug != pc_debug) + rc = -EFAULT; + else if (old_pc_debug != pc_debug) DEBUGP(0, dev, "Changed debug log level " "to %i\n", pc_debug); } - return rc; + break; #endif default: DEBUGP(4, dev, "... in default (unknown IOCTL code)\n"); - return -EINVAL; + rc = -ENOTTY; } +out: + unlock_kernel(); + return rc; } static int cmm_open(struct inode *inode, struct file *filp) @@ -1631,16 +1652,22 @@ static int cmm_open(struct inode *inode, struct file *filp) struct cm4000_dev *dev; struct pcmcia_device *link; int minor = iminor(inode); + int ret; if (minor >= CM4000_MAX_DEV) return -ENODEV; + lock_kernel(); link = dev_table[minor]; - if (link == NULL || !pcmcia_dev_present(link)) - return -ENODEV; + if (link == NULL || !pcmcia_dev_present(link)) { + ret = -ENODEV; + goto out; + } - if (link->open) - return -EBUSY; + if (link->open) { + ret = -EBUSY; + goto out; + } dev = link->priv; filp->private_data = dev; @@ -1660,8 +1687,10 @@ static int cmm_open(struct inode *inode, struct file *filp) * vaild = block until valid (or card * inserted) */ - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto out; + } dev->mdelay = T_50MSEC; @@ -1671,7 +1700,10 @@ static int cmm_open(struct inode *inode, struct file *filp) link->open = 1; /* only one open per device */ DEBUGP(2, dev, "<- cmm_open\n"); - return nonseekable_open(inode, filp); + ret = nonseekable_open(inode, filp); +out: + unlock_kernel(); + return ret; } static int cmm_close(struct inode *inode, struct file *filp) @@ -1897,7 +1929,7 @@ static const struct file_operations cm4000_fops = { .owner = THIS_MODULE, .read = cmm_read, .write = cmm_write, - .ioctl = cmm_ioctl, + .unlocked_ioctl = cmm_ioctl, .open = cmm_open, .release= cmm_close, }; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 035084c0732..6181f8a9b0b 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -26,6 +26,7 @@ #include <linux/fs.h> #include <linux/delay.h> #include <linux/poll.h> +#include <linux/smp_lock.h> #include <linux/wait.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -448,23 +449,30 @@ static int cm4040_open(struct inode *inode, struct file *filp) struct reader_dev *dev; struct pcmcia_device *link; int minor = iminor(inode); + int ret; if (minor >= CM_MAX_DEV) return -ENODEV; + lock_kernel(); link = dev_table[minor]; - if (link == NULL || !pcmcia_dev_present(link)) - return -ENODEV; + if (link == NULL || !pcmcia_dev_present(link)) { + ret = -ENODEV; + goto out; + } - if (link->open) - return -EBUSY; + if (link->open) { + ret = -EBUSY; + goto out; + } dev = link->priv; filp->private_data = dev; if (filp->f_flags & O_NONBLOCK) { DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n"); - return -EAGAIN; + ret = -EAGAIN; + goto out; } link->open = 1; @@ -473,7 +481,10 @@ static int cm4040_open(struct inode *inode, struct file *filp) mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD); DEBUGP(2, dev, "<- cm4040_open (successfully)\n"); - return nonseekable_open(inode, filp); + ret = nonseekable_open(inode, filp); +out: + unlock_kernel(); + return ret; } static int cm4040_close(struct inode *inode, struct file *filp) diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index 00c7f8407e3..cc7dcea2d28 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -28,7 +28,6 @@ #include <linux/sched.h> #include <linux/slab.h> -#include <pcmcia/version.h> #include <pcmcia/cisreg.h> #include <pcmcia/device_id.h> #include <pcmcia/ss.h> diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 3381424d70a..8dbf4d9b644 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -63,11 +63,11 @@ MODULE_LICENSE("Dual MPL/GPL"); #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) -#ifdef PCMCIA_DEBUG -INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); +#ifdef CONFIG_PCMCIA_DEBUG +INT_MODULE_PARM(pc_debug, 0); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) -static char *version = -"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)"; +/*static char *version = +"ide-cs.c 1.3 2002/10/26 05:45:31 (David Hinds)";*/ #else #define DEBUG(n, args...) #endif @@ -375,7 +375,7 @@ failed: ======================================================================*/ -void ide_release(struct pcmcia_device *link) +static void ide_release(struct pcmcia_device *link) { ide_info_t *info = link->priv; ide_hwif_t *hwif = info->hwif; diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 10748240cb2..6a866d7c8ae 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -50,17 +50,19 @@ static inline dev_info_t *which_dev(mddev_t *mddev, sector_t sector) /** * linear_mergeable_bvec -- tell bio layer if two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can take at this offset */ -static int linear_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int linear_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; dev_info_t *dev0; - unsigned long maxsectors, bio_sectors = bio->bi_size >> 9; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9; + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); dev0 = which_dev(mddev, sector); maxsectors = (dev0->size << 1) - (sector - (dev0->offset<<1)); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 914c04ddec7..bcbb82594a1 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -241,18 +241,20 @@ static int create_strip_zones (mddev_t *mddev) /** * raid0_mergeable_bvec -- tell bio layer if a two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can accept at this offset */ -static int raid0_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int raid0_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; if (max < 0) max = 0; /* bio_add cannot handle a negative return */ diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index a71277b640a..22bb2b1b886 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -439,26 +439,27 @@ static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev) /** * raid10_mergeable_bvec -- tell bio layer if a two requests can be merged * @q: request queue - * @bio: the buffer head that's been built up so far + * @bvm: properties of new bio * @biovec: the request that could be merged to it. * * Return amount of bytes we can accept at this offset * If near_copies == raid_disk, there are no striping issues, * but in that case, the function isn't called at all. */ -static int raid10_mergeable_bvec(struct request_queue *q, struct bio *bio, - struct bio_vec *bio_vec) +static int raid10_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; if (max < 0) max = 0; /* bio_add cannot handle a negative return */ - if (max <= bio_vec->bv_len && bio_sectors == 0) - return bio_vec->bv_len; + if (max <= biovec->bv_len && bio_sectors == 0) + return biovec->bv_len; else return max; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 3b27df52456..9ce7154845c 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3314,15 +3314,17 @@ static int raid5_congested(void *data, int bits) /* We want read requests to align with chunks where possible, * but write requests don't need to. */ -static int raid5_mergeable_bvec(struct request_queue *q, struct bio *bio, struct bio_vec *biovec) +static int raid5_mergeable_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) { mddev_t *mddev = q->queuedata; - sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); int max; unsigned int chunk_sectors = mddev->chunk_size >> 9; - unsigned int bio_sectors = bio->bi_size >> 9; + unsigned int bio_sectors = bvm->bi_size >> 9; - if (bio_data_dir(bio) == WRITE) + if ((bvm->bi_rw & 1) == WRITE) return biovec->bv_len; /* always allow writes to be mergeable */ max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 4a79b187b56..5c29872184e 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -130,10 +130,6 @@ typedef struct partition_t { u_int16_t DataUnits; u_int32_t BlocksPerUnit; erase_unit_header_t header; -#if 0 - region_info_t region; - memory_handle_t handle; -#endif } partition_t; /* Partition state flags */ diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 1912d968718..0cc31675aeb 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -498,17 +498,14 @@ static int pcmciamtd_config(struct pcmcia_device *link) int i; config_info_t t; static char *probes[] = { "jedec_probe", "cfi_probe" }; - cisinfo_t cisinfo; int new_name = 0; DEBUG(3, "link=0x%p", link); DEBUG(2, "Validating CIS"); - ret = pcmcia_validate_cis(link, &cisinfo); + ret = pcmcia_validate_cis(link, NULL); if(ret != CS_SUCCESS) { cs_error(link, GetTupleData, ret); - } else { - DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); } card_settings(dev, link, &new_name); @@ -563,9 +560,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); /* Get write protect status */ - CS_CHECK(GetStatus, pcmcia_get_status(link, &status)); - DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", - status.CardState, (unsigned long)link->win); + DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { err("ioremap(%lu, %u) failed", req.Base, req.Size); diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index d26f69b0184..ef671d1a3bf 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1324,7 +1324,7 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) goto fail; } - txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_KERNEL); + txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH); if (!txs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating tx ring page"); @@ -1340,7 +1340,7 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) } info->tx_ring_ref = err; - rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_KERNEL); + rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH); if (!rxs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating rx ring page"); diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 1b0eb5aaf65..e45402adac3 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -263,6 +263,13 @@ config OMAP_CF Say Y here to support the CompactFlash controller on OMAP. Note that this doesn't support "True IDE" mode. +config BFIN_CFPCMCIA + tristate "Blackfin CompactFlash PCMCIA Driver" + depends on PCMCIA && BLACKFIN + help + Say Y here to support the CompactFlash PCMCIA driver for Blackfin. + + config AT91_CF tristate "AT91 CompactFlash Controller" depends on PCMCIA && ARCH_AT91RM9200 diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 6f6478ba717..85c6cc931f9 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o obj-$(CONFIG_OMAP_CF) += omap_cf.o +obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h index 1e467bb5407..a53ef590251 100644 --- a/drivers/pcmcia/au1000_generic.h +++ b/drivers/pcmcia/au1000_generic.h @@ -26,7 +26,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" @@ -34,9 +33,9 @@ #define AU1000_PCMCIA_IO_SPEED (255) #define AU1000_PCMCIA_MEM_SPEED (300) -#define AU1X_SOCK0_IO 0xF00000000 -#define AU1X_SOCK0_PHYS_ATTR 0xF40000000 -#define AU1X_SOCK0_PHYS_MEM 0xF80000000 +#define AU1X_SOCK0_IO 0xF00000000ULL +#define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL +#define AU1X_SOCK0_PHYS_MEM 0xF80000000ULL /* pseudo 32 bit phys addresses, which get fixed up to the * real 36 bit address in fixup_bigphys_addr() */ #define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000 @@ -45,16 +44,20 @@ /* pcmcia socket 1 needs external glue logic so the memory map * differs from board to board. */ -#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_PB1200) -#define AU1X_SOCK1_IO 0xF08000000 -#define AU1X_SOCK1_PHYS_ATTR 0xF48000000 -#define AU1X_SOCK1_PHYS_MEM 0xF88000000 +#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || \ + defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || \ + defined(CONFIG_MIPS_PB1200) +#define AU1X_SOCK1_IO 0xF08000000ULL +#define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL +#define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL #define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000 #define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8800000 -#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || defined(CONFIG_MIPS_DB1200) -#define AU1X_SOCK1_IO 0xF04000000 -#define AU1X_SOCK1_PHYS_ATTR 0xF44000000 -#define AU1X_SOCK1_PHYS_MEM 0xF84000000 +#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || \ + defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || \ + defined(CONFIG_MIPS_DB1200) +#define AU1X_SOCK1_IO 0xF04000000ULL +#define AU1X_SOCK1_PHYS_ATTR 0xF44000000ULL +#define AU1X_SOCK1_PHYS_MEM 0xF84000000ULL #define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000 #define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8400000 #endif diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c index 157e41423a0..aa1cd4d3aa2 100644 --- a/drivers/pcmcia/au1000_pb1x00.c +++ b/drivers/pcmcia/au1000_pb1x00.c @@ -35,7 +35,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/bus_ops.h> #include "cs_internal.h" diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c index c78ed534751..8a9b18cee84 100644 --- a/drivers/pcmcia/au1000_xxs1500.c +++ b/drivers/pcmcia/au1000_xxs1500.c @@ -39,7 +39,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/bus_ops.h> #include "cs_internal.h" diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c new file mode 100644 index 00000000000..bb7338863fb --- /dev/null +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -0,0 +1,339 @@ +/* + * file: drivers/pcmcia/bfin_cf.c + * + * based on: drivers/pcmcia/omap_cf.c + * omap_cf.c -- OMAP 16xx CompactFlash controller driver + * + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc. + * + * bugs: enter bugs at http://blackfin.uclinux.org/ + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2, or (at your option) + * any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; see the file copying. + * if not, write to the free software foundation, + * 59 temple place - suite 330, boston, ma 02111-1307, usa. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> + +#include <pcmcia/ss.h> +#include <pcmcia/cisreg.h> +#include <asm/gpio.h> + +#define SZ_1K 0x00000400 +#define SZ_8K 0x00002000 +#define SZ_2K (2 * SZ_1K) + +#define POLL_INTERVAL (2 * HZ) + +#define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */ +#define CF_ATASEL_DIS 0x20311800 + +#define bfin_cf_present(pfx) (gpio_get_value(pfx)) + +/*--------------------------------------------------------------------------*/ + +static const char driver_name[] = "bfin_cf_pcmcia"; + +struct bfin_cf_socket { + struct pcmcia_socket socket; + + struct timer_list timer; + unsigned present:1; + unsigned active:1; + + struct platform_device *pdev; + unsigned long phys_cf_io; + unsigned long phys_cf_attr; + u_int irq; + u_short cd_pfx; +}; + +/*--------------------------------------------------------------------------*/ +static int bfin_cf_reset(void) +{ + outw(0, CF_ATASEL_ENA); + mdelay(200); + outw(0, CF_ATASEL_DIS); + + return 0; +} + +static int bfin_cf_ss_init(struct pcmcia_socket *s) +{ + return 0; +} + +/* the timer is primarily to kick this socket's pccardd */ +static void bfin_cf_timer(unsigned long _cf) +{ + struct bfin_cf_socket *cf = (void *)_cf; + unsigned short present = bfin_cf_present(cf->cd_pfx); + + if (present != cf->present) { + cf->present = present; + dev_dbg(&cf->pdev->dev, ": card %s\n", + present ? "present" : "gone"); + pcmcia_parse_events(&cf->socket, SS_DETECT); + } + + if (cf->active) + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); +} + +static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) +{ + struct bfin_cf_socket *cf; + + if (!sp) + return -EINVAL; + + cf = container_of(s, struct bfin_cf_socket, socket); + + if (bfin_cf_present(cf->cd_pfx)) { + *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; + s->irq.AssignedIRQ = 0; + s->pci_irq = cf->irq; + + } else + *sp = 0; + return 0; +} + +static int +bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) +{ + + struct bfin_cf_socket *cf; + cf = container_of(sock, struct bfin_cf_socket, socket); + + switch (s->Vcc) { + case 0: + case 33: + break; + case 50: + break; + default: + return -EINVAL; + } + + if (s->flags & SS_RESET) { + disable_irq(cf->irq); + bfin_cf_reset(); + enable_irq(cf->irq); + } + + dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n", + s->Vcc, s->io_irq, s->flags, s->csc_mask); + + return 0; +} + +static int bfin_cf_ss_suspend(struct pcmcia_socket *s) +{ + return bfin_cf_set_socket(s, &dead_socket); +} + +/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */ + +static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) +{ + struct bfin_cf_socket *cf; + + cf = container_of(s, struct bfin_cf_socket, socket); + io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT; + io->start = cf->phys_cf_io; + io->stop = io->start + SZ_2K - 1; + return 0; +} + +static int +bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) +{ + struct bfin_cf_socket *cf; + + if (map->card_start) + return -EINVAL; + cf = container_of(s, struct bfin_cf_socket, socket); + map->static_start = cf->phys_cf_io; + map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT; + if (map->flags & MAP_ATTRIB) + map->static_start = cf->phys_cf_attr; + + return 0; +} + +static struct pccard_operations bfin_cf_ops = { + .init = bfin_cf_ss_init, + .suspend = bfin_cf_ss_suspend, + .get_status = bfin_cf_get_status, + .set_socket = bfin_cf_set_socket, + .set_io_map = bfin_cf_set_io_map, + .set_mem_map = bfin_cf_set_mem_map, +}; + +/*--------------------------------------------------------------------------*/ + +static int __devinit bfin_cf_probe(struct platform_device *pdev) +{ + struct bfin_cf_socket *cf; + struct resource *io_mem, *attr_mem; + int irq; + unsigned short cd_pfx; + int status = 0; + + dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n"); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */ + + if (gpio_request(cd_pfx, "pcmcia: CD")) { + dev_err(&pdev->dev, + "Failed ro request Card Detect GPIO_%d\n", + cd_pfx); + return -EBUSY; + } + gpio_direction_input(cd_pfx); + + cf = kzalloc(sizeof *cf, GFP_KERNEL); + if (!cf) { + gpio_free(cd_pfx); + return -ENOMEM; + } + + cf->cd_pfx = cd_pfx; + + setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf); + + cf->pdev = pdev; + platform_set_drvdata(pdev, cf); + + cf->irq = irq; + cf->socket.pci_irq = irq; + + set_irq_type(irq, IRQF_TRIGGER_LOW); + + io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!io_mem || !attr_mem) + goto fail0; + + cf->phys_cf_io = io_mem->start; + cf->phys_cf_attr = attr_mem->start; + + /* pcmcia layer only remaps "real" memory */ + cf->socket.io_offset = (unsigned long) + ioremap(cf->phys_cf_io, SZ_2K); + + if (!cf->socket.io_offset) + goto fail0; + + dev_err(&pdev->dev, ": on irq %d\n", irq); + + dev_dbg(&pdev->dev, ": %s\n", + bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)"); + + cf->socket.owner = THIS_MODULE; + cf->socket.dev.parent = &pdev->dev; + cf->socket.ops = &bfin_cf_ops; + cf->socket.resource_ops = &pccard_static_ops; + cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP + | SS_CAP_MEM_ALIGN; + cf->socket.map_size = SZ_2K; + + status = pcmcia_register_socket(&cf->socket); + if (status < 0) + goto fail2; + + cf->active = 1; + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); + return 0; + +fail2: + iounmap((void __iomem *)cf->socket.io_offset); + release_mem_region(cf->phys_cf_io, SZ_8K); + +fail0: + gpio_free(cf->cd_pfx); + kfree(cf); + platform_set_drvdata(pdev, NULL); + + return status; +} + +static int __devexit bfin_cf_remove(struct platform_device *pdev) +{ + struct bfin_cf_socket *cf = platform_get_drvdata(pdev); + + gpio_free(cf->cd_pfx); + cf->active = 0; + pcmcia_unregister_socket(&cf->socket); + del_timer_sync(&cf->timer); + iounmap((void __iomem *)cf->socket.io_offset); + release_mem_region(cf->phys_cf_io, SZ_8K); + platform_set_drvdata(pdev, NULL); + kfree(cf); + return 0; +} + +static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, mesg); +} + +static int bfin_cf_resume(struct platform_device *pdev) +{ + return pcmcia_socket_dev_resume(&pdev->dev); +} + +static struct platform_driver bfin_cf_driver = { + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + }, + .probe = bfin_cf_probe, + .remove = __devexit_p(bfin_cf_remove), + .suspend = bfin_cf_suspend, + .resume = bfin_cf_resume, +}; + +static int __init bfin_cf_init(void) +{ + return platform_driver_register(&bfin_cf_driver); +} + +static void __exit bfin_cf_exit(void) +{ + platform_driver_unregister(&bfin_cf_driver); +} + +module_init(bfin_cf_init); +module_exit(bfin_cf_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>") +MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index fb2f38dc92c..911ca0e8dfc 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -30,11 +30,9 @@ #include <asm/irq.h> #include <asm/io.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 36379535f9d..9fcff0c3361 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -30,7 +30,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cisreg.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" @@ -1439,10 +1438,11 @@ EXPORT_SYMBOL(pccard_read_tuple); ======================================================================*/ -int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info) +int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *info) { tuple_t *tuple; cisparse_t *p; + unsigned int count = 0; int ret, reserved, dev_ok = 0, ident_ok = 0; if (!s) @@ -1457,7 +1457,7 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_ return CS_OUT_OF_RESOURCE; } - info->Chains = reserved = 0; + count = reserved = 0; tuple->DesiredTuple = RETURN_FIRST_TUPLE; tuple->Attributes = TUPLE_RETURN_COMMON; ret = pccard_get_first_tuple(s, function, tuple); @@ -1482,7 +1482,7 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_ if (!dev_ok && !ident_ok) goto done; - for (info->Chains = 1; info->Chains < MAX_TUPLES; info->Chains++) { + for (count = 1; count < MAX_TUPLES; count++) { ret = pccard_get_next_tuple(s, function, tuple); if (ret != CS_SUCCESS) break; if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || @@ -1490,11 +1490,13 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_ ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) reserved++; } - if ((info->Chains == MAX_TUPLES) || (reserved > 5) || - ((!dev_ok || !ident_ok) && (info->Chains > 10))) - info->Chains = 0; + if ((count) || (reserved > 5) || + ((!dev_ok || !ident_ok) && (count > 10))) + count = 0; done: + if (info) + *info = count; kfree(tuple); kfree(p); return CS_SUCCESS; diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 29276bd2829..d1207393fc3 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -32,11 +32,9 @@ #include <asm/system.h> #include <asm/irq.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> @@ -238,7 +236,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) init_completion(&socket->socket_released); init_completion(&socket->thread_done); - init_waitqueue_head(&socket->thread_wait); mutex_init(&socket->skt_mutex); spin_lock_init(&socket->thread_lock); @@ -278,10 +275,9 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) cs_dbg(socket, 0, "pcmcia_unregister_socket(0x%p)\n", socket->ops); - if (socket->thread) { - wake_up(&socket->thread_wait); + if (socket->thread) kthread_stop(socket->thread); - } + release_cis_mem(socket); /* remove from our own list */ @@ -635,7 +631,6 @@ static void socket_detect_change(struct pcmcia_socket *skt) static int pccardd(void *__skt) { struct pcmcia_socket *skt = __skt; - DECLARE_WAITQUEUE(wait, current); int ret; skt->thread = current; @@ -656,7 +651,6 @@ static int pccardd(void *__skt) if (ret) dev_warn(&skt->dev, "err %d adding socket attributes\n", ret); - add_wait_queue(&skt->thread_wait, &wait); complete(&skt->thread_done); set_freezable(); @@ -694,8 +688,6 @@ static int pccardd(void *__skt) /* make sure we are running before we exit */ set_current_state(TASK_RUNNING); - remove_wait_queue(&skt->thread_wait, &wait); - /* remove from the device core */ pccard_sysfs_remove_socket(&skt->dev); device_unregister(&skt->dev); @@ -716,7 +708,7 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) s->thread_events |= events; spin_unlock_irqrestore(&s->thread_lock, flags); - wake_up(&s->thread_wait); + wake_up_process(s->thread); } } /* pcmcia_parse_events */ EXPORT_SYMBOL(pcmcia_parse_events); diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index e7d5d141f24..63dc1a28bda 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -26,18 +26,6 @@ #define CLIENT_WIN_REQ(i) (0x1<<(i)) #define CLIENT_CARDBUS 0x8000 -#define REGION_MAGIC 0xE3C9 -typedef struct region_t { - u_short region_magic; - u_short state; - dev_info_t dev_info; - struct pcmcia_device *mtd; - u_int MediaID; - region_info_t info; -} region_t; - -#define REGION_STALE 0x01 - /* Each card function gets one of these guys */ typedef struct config_t { struct kref ref; @@ -130,7 +118,6 @@ extern struct list_head pcmcia_socket_list; int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req); int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config); int pccard_reset_card(struct pcmcia_socket *skt); -int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status); struct pcmcia_callback{ diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index e40775443d0..4174d9656e3 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -25,7 +25,6 @@ #include <linux/kref.h> #include <linux/dma-mapping.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> @@ -741,9 +740,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f static int pcmcia_card_add(struct pcmcia_socket *s) { - cisinfo_t cisinfo; cistpl_longlink_mfc_t mfc; - unsigned int no_funcs, i; + unsigned int no_funcs, i, no_chains; int ret = 0; if (!(s->resource_setup_done)) { @@ -757,8 +755,8 @@ static int pcmcia_card_add(struct pcmcia_socket *s) return -EAGAIN; /* try again, but later... */ } - ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo); - if (ret || !cisinfo.Chains) { + ret = pccard_validate_cis(s, BIND_FN_ALL, &no_chains); + if (ret || !no_chains) { ds_dbg(0, "invalid CIS or invalid resources\n"); return -ENODEV; } @@ -852,7 +850,7 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { struct pcmcia_socket *s = dev->socket; const struct firmware *fw; - char path[20]; + char path[FIRMWARE_NAME_MAX]; int ret = -ENOMEM; int no_funcs; int old_funcs; @@ -864,7 +862,7 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) ds_dbg(1, "trying to load CIS file %s\n", filename); - if (strlen(filename) > 14) { + if (strlen(filename) > (FIRMWARE_NAME_MAX - 1)) { printk(KERN_WARNING "pcmcia: CIS filename is too long [%s]\n", filename); return -EINVAL; diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c index f2e810f53c8..fb2bc1fb015 100644 --- a/drivers/pcmcia/hd64465_ss.c +++ b/drivers/pcmcia/hd64465_ss.c @@ -1,6 +1,4 @@ /* - * $Id: hd64465_ss.c,v 1.7 2003/07/06 14:42:50 lethal Exp $ - * * Device driver for the PCMCIA controller module of the * Hitachi HD64465 handheld companion chip. * @@ -48,7 +46,6 @@ #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include "cs_internal.h" #define MODNAME "hd64465_ss" diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index e13618656ff..46561face12 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -5,8 +5,6 @@ * * Author: Arjan Van De Ven <arjanv@redhat.com> * Loosly based on i82365.c from the pcmcia-cs package - * - * $Id: i82092aa.c,v 1.2 2001/10/23 14:43:34 arjanv Exp $ */ #include <linux/kernel.h> diff --git a/drivers/pcmcia/i82092aa.h b/drivers/pcmcia/i82092aa.h index b0d453303c5..8836d393ad0 100644 --- a/drivers/pcmcia/i82092aa.h +++ b/drivers/pcmcia/i82092aa.h @@ -3,8 +3,6 @@ #include <linux/interrupt.h> -/* $Id: i82092aa.h,v 1.1.1.1 2001/09/19 14:53:15 dwmw2 Exp $ */ - /* Debuging defines */ #ifdef NOTRACE #define enter(x) printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__) diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 32a2ab11979..68f6b2702bc 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -1263,7 +1263,7 @@ static int __init init_i82365(void) ret = driver_register(&i82365_driver); if (ret) - return ret; + goto err_out; i82365_device = platform_device_alloc("i82365", 0); if (i82365_device) { @@ -1273,10 +1273,8 @@ static int __init init_i82365(void) } else ret = -ENOMEM; - if (ret) { - driver_unregister(&i82365_driver); - return ret; - } + if (ret) + goto err_driver_unregister; printk(KERN_INFO "Intel ISA PCIC probe: "); sockets = 0; @@ -1285,16 +1283,17 @@ static int __init init_i82365(void) if (sockets == 0) { printk("not found.\n"); - platform_device_unregister(i82365_device); - release_region(i365_base, 2); - driver_unregister(&i82365_driver); - return -ENODEV; + ret = -ENODEV; + goto err_dev_unregister; } /* Set up interrupt handler(s) */ if (grab_irq != 0) - request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); - + ret = request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); + + if (ret) + goto err_socket_release; + /* register sockets with the pcmcia core */ for (i = 0; i < sockets; i++) { socket[i].socket.dev.parent = &i82365_device->dev; @@ -1324,7 +1323,23 @@ static int __init init_i82365(void) } return 0; - +err_socket_release: + for (i = 0; i < sockets; i++) { + /* Turn off all interrupt sources! */ + i365_set(i, I365_CSCINT, 0); + release_region(socket[i].ioaddr, 2); + } +err_dev_unregister: + platform_device_unregister(i82365_device); + release_region(i365_base, 2); +#ifdef CONFIG_PNP + if (i82365_pnpdev) + pnp_disable_dev(i82365_pnpdev); +#endif +err_driver_unregister: + driver_unregister(&i82365_driver); +err_out: + return ret; } /* init_i82365 */ static void __exit exit_i82365(void) diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index ac70d2cb7dd..13a5fbd50a0 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1,7 +1,7 @@ /* * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series. * - * (C) 1999-2000 Magnus Damm <damm@bitsmart.com> + * (C) 1999-2000 Magnus Damm <damm@opensource.se> * (C) 2001-2002 Montavista Software, Inc. * <mlocke@mvista.com> * @@ -60,7 +60,6 @@ #include <asm/of_device.h> #include <asm/of_platform.h> -#include <pcmcia/version.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/ss.h> diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 5f186abca10..afd00e7bbbe 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -29,10 +29,10 @@ #include <linux/pci.h> #include <linux/workqueue.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> #include <pcmcia/ds.h> #include <pcmcia/ss.h> @@ -138,6 +138,154 @@ static int proc_read_drivers(char *buf, char **start, off_t pos, } #endif + +#ifdef CONFIG_PCMCIA_PROBE + +static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) +{ + int irq; + u32 mask; + + irq = adj->resource.irq.IRQ; + if ((irq < 0) || (irq > 15)) + return CS_BAD_IRQ; + + if (adj->Action != REMOVE_MANAGED_RESOURCE) + return 0; + + mask = 1 << irq; + + if (!(s->irq_mask & mask)) + return 0; + + s->irq_mask &= ~mask; + + return 0; +} + +#else + +static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) { + return CS_SUCCESS; +} + +#endif + +static int pcmcia_adjust_resource_info(adjust_t *adj) +{ + struct pcmcia_socket *s; + int ret = CS_UNSUPPORTED_FUNCTION; + unsigned long flags; + + down_read(&pcmcia_socket_list_rwsem); + list_for_each_entry(s, &pcmcia_socket_list, socket_list) { + + if (adj->Resource == RES_IRQ) + ret = adjust_irq(s, adj); + + else if (s->resource_ops->add_io) { + unsigned long begin, end; + + /* you can't use the old interface if the new + * one was used before */ + spin_lock_irqsave(&s->lock, flags); + if ((s->resource_setup_new) && + !(s->resource_setup_old)) { + spin_unlock_irqrestore(&s->lock, flags); + continue; + } else if (!(s->resource_setup_old)) + s->resource_setup_old = 1; + spin_unlock_irqrestore(&s->lock, flags); + + switch (adj->Resource) { + case RES_MEMORY_RANGE: + begin = adj->resource.memory.Base; + end = adj->resource.memory.Base + adj->resource.memory.Size - 1; + if (s->resource_ops->add_mem) + ret =s->resource_ops->add_mem(s, adj->Action, begin, end); + case RES_IO_RANGE: + begin = adj->resource.io.BasePort; + end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; + if (s->resource_ops->add_io) + ret = s->resource_ops->add_io(s, adj->Action, begin, end); + } + if (!ret) { + /* as there's no way we know this is the + * last call to adjust_resource_info, we + * always need to assume this is the latest + * one... */ + spin_lock_irqsave(&s->lock, flags); + s->resource_setup_done = 1; + spin_unlock_irqrestore(&s->lock, flags); + } + } + } + up_read(&pcmcia_socket_list_rwsem); + + return (ret); +} + +/** pccard_get_status + * + * Get the current socket state bits. We don't support the latched + * SocketState yet: I haven't seen any point for it. + */ + +static int pccard_get_status(struct pcmcia_socket *s, + struct pcmcia_device *p_dev, + cs_status_t *status) +{ + config_t *c; + int val; + + s->ops->get_status(s, &val); + status->CardState = status->SocketState = 0; + status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0; + status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0; + status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0; + status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0; + if (s->state & SOCKET_SUSPEND) + status->CardState |= CS_EVENT_PM_SUSPEND; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + c = (p_dev) ? p_dev->function_config : NULL; + + if ((c != NULL) && (c->state & CONFIG_LOCKED) && + (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { + u_char reg; + if (c->CardValues & PRESENT_PIN_REPLACE) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + status->CardState |= + (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; + status->CardState |= + (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0; + status->CardState |= + (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0; + status->CardState |= + (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0; + } else { + /* No PRR? Then assume we're always ready */ + status->CardState |= CS_EVENT_READY_CHANGE; + } + if (c->CardValues & PRESENT_EXT_STATUS) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + status->CardState |= + (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; + } + return CS_SUCCESS; + } + status->CardState |= + (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0; + status->CardState |= + (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0; + status->CardState |= + (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0; + status->CardState |= + (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; + return CS_SUCCESS; +} /* pccard_get_status */ + /*====================================================================== These manage a ring buffer of events pending for one user process @@ -546,8 +694,6 @@ static u_int ds_poll(struct file *file, poll_table *wait) /*====================================================================*/ -extern int pcmcia_adjust_resource_info(adjust_t *adj); - static int ds_ioctl(struct inode * inode, struct file * file, u_int cmd, u_long arg) { @@ -649,7 +795,7 @@ static int ds_ioctl(struct inode * inode, struct file * file, mutex_lock(&s->skt_mutex); pcmcia_validate_mem(s); mutex_unlock(&s->skt_mutex); - ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo); + ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo.Chains); break; case DS_SUSPEND_CARD: ret = pcmcia_suspend_card(s); diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 1d128fbd1a9..4884a18cf9e 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -21,11 +21,9 @@ #include <linux/pci.h> #include <linux/device.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> @@ -311,74 +309,6 @@ int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, EXPORT_SYMBOL(pcmcia_get_window); -/** pccard_get_status - * - * Get the current socket state bits. We don't support the latched - * SocketState yet: I haven't seen any point for it. - */ - -int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, - cs_status_t *status) -{ - config_t *c; - int val; - - s->ops->get_status(s, &val); - status->CardState = status->SocketState = 0; - status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0; - status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0; - status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0; - status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0; - if (s->state & SOCKET_SUSPEND) - status->CardState |= CS_EVENT_PM_SUSPEND; - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - c = (p_dev) ? p_dev->function_config : NULL; - - if ((c != NULL) && (c->state & CONFIG_LOCKED) && - (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { - u_char reg; - if (c->CardValues & PRESENT_PIN_REPLACE) { - pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); - status->CardState |= - (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; - status->CardState |= - (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0; - status->CardState |= - (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0; - status->CardState |= - (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0; - } else { - /* No PRR? Then assume we're always ready */ - status->CardState |= CS_EVENT_READY_CHANGE; - } - if (c->CardValues & PRESENT_EXT_STATUS) { - pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); - status->CardState |= - (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; - } - return CS_SUCCESS; - } - status->CardState |= - (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0; - status->CardState |= - (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0; - status->CardState |= - (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0; - status->CardState |= - (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; - return CS_SUCCESS; -} /* pccard_get_status */ - -int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status) -{ - return pccard_get_status(p_dev->socket, p_dev, status); -} -EXPORT_SYMBOL(pcmcia_get_status); - - - /** pcmcia_get_mem_page * * Change the card address of an already open memory window. @@ -812,6 +742,15 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) type = IRQF_SHARED; #ifdef CONFIG_PCMCIA_PROBE + +#ifdef IRQ_NOAUTOEN + /* if the underlying IRQ infrastructure allows for it, only allocate + * the IRQ, but do not enable it + */ + if (!(req->Attributes & IRQ_HANDLE_PRESENT)) + type |= IRQ_NOAUTOEN; +#endif /* IRQ_NOAUTOEN */ + if (s->irq.AssignedIRQ != 0) { /* If the interrupt is already assigned, it must be the same */ irq = s->irq.AssignedIRQ; @@ -966,7 +905,7 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) { pcmcia_release_configuration(p_dev); pcmcia_release_io(p_dev, &p_dev->io); pcmcia_release_irq(p_dev, &p_dev->irq); - if (&p_dev->win) + if (p_dev->win) pcmcia_release_window(p_dev->win); } EXPORT_SYMBOL(pcmcia_disable_device); diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 9414163c78e..ccfdf1969a7 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -33,7 +33,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index ce2226273aa..c0e2afc79e3 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -21,86 +21,6 @@ #include "cs_internal.h" -#ifdef CONFIG_PCMCIA_IOCTL - -#ifdef CONFIG_PCMCIA_PROBE - -static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) -{ - int irq; - u32 mask; - - irq = adj->resource.irq.IRQ; - if ((irq < 0) || (irq > 15)) - return CS_BAD_IRQ; - - if (adj->Action != REMOVE_MANAGED_RESOURCE) - return 0; - - mask = 1 << irq; - - if (!(s->irq_mask & mask)) - return 0; - - s->irq_mask &= ~mask; - - return 0; -} - -#else - -static inline int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) { - return CS_SUCCESS; -} - -#endif - - -int pcmcia_adjust_resource_info(adjust_t *adj) -{ - struct pcmcia_socket *s; - int ret = CS_UNSUPPORTED_FUNCTION; - unsigned long flags; - - down_read(&pcmcia_socket_list_rwsem); - list_for_each_entry(s, &pcmcia_socket_list, socket_list) { - - if (adj->Resource == RES_IRQ) - ret = adjust_irq(s, adj); - - else if (s->resource_ops->adjust_resource) { - - /* you can't use the old interface if the new - * one was used before */ - spin_lock_irqsave(&s->lock, flags); - if ((s->resource_setup_new) && - !(s->resource_setup_old)) { - spin_unlock_irqrestore(&s->lock, flags); - continue; - } else if (!(s->resource_setup_old)) - s->resource_setup_old = 1; - spin_unlock_irqrestore(&s->lock, flags); - - ret = s->resource_ops->adjust_resource(s, adj); - if (!ret) { - /* as there's no way we know this is the - * last call to adjust_resource_info, we - * always need to assume this is the latest - * one... */ - spin_lock_irqsave(&s->lock, flags); - s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); - } - } - } - up_read(&pcmcia_socket_list_rwsem); - - return (ret); -} -EXPORT_SYMBOL(pcmcia_adjust_resource_info); - -#endif - int pcmcia_validate_mem(struct pcmcia_socket *s) { if (s->resource_ops->validate_mem) @@ -164,7 +84,8 @@ struct pccard_resource_ops pccard_static_ops = { .adjust_io_region = NULL, .find_io = NULL, .find_mem = NULL, - .adjust_resource = NULL, + .add_io = NULL, + .add_mem = NULL, .init = static_init, .exit = NULL, }; @@ -264,7 +185,8 @@ struct pccard_resource_ops pccard_iodyn_ops = { .adjust_io_region = iodyn_adjust_io_region, .find_io = iodyn_find_io_region, .find_mem = NULL, - .adjust_resource = NULL, + .add_io = NULL, + .add_mem = NULL, .init = static_init, .exit = NULL, }; diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 0fcf763b917..d0c1d63d189 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -31,7 +31,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" @@ -261,21 +260,22 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, ======================================================================*/ /* Validation function for cards with a valid CIS */ -static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info) +static int readable(struct pcmcia_socket *s, struct resource *res, + unsigned int *count) { int ret = -1; s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { - ret = pccard_validate_cis(s, BIND_FN_ALL, info); + ret = pccard_validate_cis(s, BIND_FN_ALL, count); /* invalidate mapping and CIS cache */ iounmap(s->cis_virt); s->cis_virt = NULL; destroy_cis_cache(s); } s->cis_mem.res = NULL; - if ((ret != 0) || (info->Chains == 0)) + if ((ret != 0) || (count == 0)) return 0; return 1; } @@ -316,7 +316,7 @@ static int cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) { struct resource *res1, *res2; - cisinfo_t info1, info2; + unsigned int info1, info2; int ret = 0; res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "cs memory probe"); @@ -330,7 +330,7 @@ cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) free_region(res2); free_region(res1); - return (ret == 2) && (info1.Chains == info2.Chains); + return (ret == 2) && (info1 == info2); } static int @@ -766,21 +766,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long } -static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) -{ - unsigned long end; - - switch (adj->Resource) { - case RES_MEMORY_RANGE: - end = adj->resource.memory.Base + adj->resource.memory.Size - 1; - return adjust_memory(s, adj->Action, adj->resource.memory.Base, end); - case RES_IO_RANGE: - end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; - return adjust_io(s, adj->Action, adj->resource.io.BasePort, end); - } - return CS_UNSUPPORTED_FUNCTION; -} - #ifdef CONFIG_PCI static int nonstatic_autoadd_resources(struct pcmcia_socket *s) { @@ -889,7 +874,8 @@ struct pccard_resource_ops pccard_nonstatic_ops = { .adjust_io_region = nonstatic_adjust_io_region, .find_io = nonstatic_find_io_region, .find_mem = nonstatic_find_mem_region, - .adjust_resource = nonstatic_adjust_resource_info, + .add_io = adjust_io, + .add_mem = adjust_memory, .init = nonstatic_init, .exit = nonstatic_release_resource_db, }; @@ -1008,41 +994,34 @@ static ssize_t store_mem_db(struct device *dev, } static DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db); -static struct device_attribute *pccard_rsrc_attributes[] = { - &dev_attr_available_resources_io, - &dev_attr_available_resources_mem, +static struct attribute *pccard_rsrc_attributes[] = { + &dev_attr_available_resources_io.attr, + &dev_attr_available_resources_mem.attr, NULL, }; +static const struct attribute_group rsrc_attributes = { + .attrs = pccard_rsrc_attributes, +}; + static int __devinit pccard_sysfs_add_rsrc(struct device *dev, struct class_interface *class_intf) { struct pcmcia_socket *s = dev_get_drvdata(dev); - struct device_attribute **attr; - int ret = 0; + if (s->resource_ops != &pccard_nonstatic_ops) return 0; - - for (attr = pccard_rsrc_attributes; *attr; attr++) { - ret = device_create_file(dev, *attr); - if (ret) - break; - } - - return ret; + return sysfs_create_group(&dev->kobj, &rsrc_attributes); } static void __devexit pccard_sysfs_remove_rsrc(struct device *dev, struct class_interface *class_intf) { struct pcmcia_socket *s = dev_get_drvdata(dev); - struct device_attribute **attr; if (s->resource_ops != &pccard_nonstatic_ops) return; - - for (attr = pccard_rsrc_attributes; *attr; attr++) - device_remove_file(dev, *attr); + sysfs_remove_group(&dev->kobj, &rsrc_attributes); } static struct class_interface pccard_rsrc_interface __refdata = { diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h index 1edc1da9d35..91ef6a0da3a 100644 --- a/drivers/pcmcia/soc_common.h +++ b/drivers/pcmcia/soc_common.h @@ -14,7 +14,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/ss.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include "cs_internal.h" diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 562384d6f32..006a29e91d8 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -27,11 +27,9 @@ #include <asm/system.h> #include <asm/irq.h> -#define IN_CARD_SERVICES #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> @@ -293,7 +291,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj, count = 0; else { struct pcmcia_socket *s; - cisinfo_t cisinfo; + unsigned int chains; if (off + count > size) count = size - off; @@ -302,9 +300,9 @@ static ssize_t pccard_show_cis(struct kobject *kobj, if (!(s->state & SOCKET_PRESENT)) return -ENODEV; - if (pccard_validate_cis(s, BIND_FN_ALL, &cisinfo)) + if (pccard_validate_cis(s, BIND_FN_ALL, &chains)) return -EIO; - if (!cisinfo.Chains) + if (!chains) return -ENODATA; count = pccard_extract_cis(s, buf, off, count); diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index d29657bf1b4..129db7bd06c 100644 --- a/drivers/pcmcia/ti113x.h +++ b/drivers/pcmcia/ti113x.h @@ -155,7 +155,7 @@ #define ENE_TEST_C9_TLTENABLE 0x02 #define ENE_TEST_C9_PFENABLE_F0 0x04 #define ENE_TEST_C9_PFENABLE_F1 0x08 -#define ENE_TEST_C9_PFENABLE (ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F0) +#define ENE_TEST_C9_PFENABLE (ENE_TEST_C9_PFENABLE_F0 | ENE_TEST_C9_PFENABLE_F1) #define ENE_TEST_C9_WPDISALBLE_F0 0x40 #define ENE_TEST_C9_WPDISALBLE_F1 0x80 #define ENE_TEST_C9_WPDISALBLE (ENE_TEST_C9_WPDISALBLE_F0 | ENE_TEST_C9_WPDISALBLE_F1) @@ -692,7 +692,7 @@ static int ti12xx_2nd_slot_empty(struct yenta_socket *socket) goto out; /* check state */ - yenta_get_status(&socket->socket, &state); + yenta_get_status(&slot2->socket, &state); if (state & SS_DETECT) { ret = 0; goto out; diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 1a402568336..1b6c52ef733 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -995,14 +995,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, now = get_clock(); DBF_EVENT(DBF_ERR, "Interrupt: bus_id %s CS/DS %04x ip %08x", - cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat), - (unsigned int) intparm); + cdev->dev.bus_id, ((irb->scsw.cmd.cstat << 8) | + irb->scsw.cmd.dstat), (unsigned int) intparm); /* check for unsolicited interrupts */ cqr = (struct dasd_ccw_req *) intparm; - if (!cqr || ((irb->scsw.cc == 1) && - (irb->scsw.fctl & SCSW_FCTL_START_FUNC) && - (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) { + if (!cqr || ((irb->scsw.cmd.cc == 1) && + (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) && + (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) { if (cqr && cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_QUEUED; device = dasd_device_from_cdev_locked(cdev); @@ -1025,7 +1025,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* Check for clear pending */ if (cqr->status == DASD_CQR_CLEAR_PENDING && - irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { + irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) { cqr->status = DASD_CQR_CLEARED; dasd_device_clear_timer(device); wake_up(&dasd_flush_wq); @@ -1041,11 +1041,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p", - ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr); + ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr); next = NULL; expires = 0; - if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && - irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) { + if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) { /* request was completed successfully */ cqr->status = DASD_CQR_SUCCESS; cqr->stopclk = now; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index e6700df52df..5c6e6f331cb 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1572,7 +1572,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* determine the address of the CCW to be restarted */ /* Imprecise ending is not set -> addr from IRB-SCSW */ - cpa = default_erp->refers->irb.scsw.cpa; + cpa = default_erp->refers->irb.scsw.cmd.cpa; if (cpa == 0) { @@ -1725,7 +1725,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) /* determine the address of the CCW to be restarted */ /* Imprecise ending is not set -> addr from IRB-SCSW */ - cpa = previous_erp->irb.scsw.cpa; + cpa = previous_erp->irb.scsw.cmd.cpa; if (cpa == 0) { @@ -2171,7 +2171,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp) { struct dasd_device *device = erp->startdev; - if (erp->refers->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK + if (erp->refers->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_CTRL_CHK)) { DEV_MESSAGE(KERN_DEBUG, device, "%s", "channel or interface control check"); @@ -2352,9 +2352,9 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2) if ((cqr1->irb.esw.esw0.erw.cons == 0) && (cqr2->irb.esw.esw0.erw.cons == 0)) { - if ((cqr1->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK | + if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_CTRL_CHK)) == - (cqr2->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK | + (cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_CTRL_CHK))) return 1; /* match with ifcc*/ } @@ -2622,8 +2622,9 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } /* double-check if current erp/cqr was successfull */ - if ((cqr->irb.scsw.cstat == 0x00) && - (cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) { + if ((cqr->irb.scsw.cmd.cstat == 0x00) && + (cqr->irb.scsw.cmd.dstat == + (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) { DEV_MESSAGE(KERN_DEBUG, device, "ERP called for successful request %p" diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index a0edae091b5..e0b77210d37 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1404,13 +1404,14 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.dstat & mask) == mask) { + if ((irb->scsw.cmd.dstat & mask) == mask) { dasd_generic_handle_state_change(device); return; } /* summary unit check */ - if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) { + if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && + (irb->ecw[7] == 0x0D)) { dasd_alias_handle_summary_unit_check(device, irb); return; } @@ -2068,11 +2069,11 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, device->cdev->dev.bus_id); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " in req: %p CS: 0x%02X DS: 0x%02X\n", req, - irb->scsw.cstat, irb->scsw.dstat); + irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing CCW: %p\n", device->cdev->dev.bus_id, - (void *) (addr_t) irb->scsw.cpa); + (void *) (addr_t) irb->scsw.cmd.cpa); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER @@ -2122,7 +2123,8 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, /* scsw->cda is either valid or zero */ len = 0; from = ++to; - fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */ + fail = (struct ccw1 *)(addr_t) + irb->scsw.cmd.cpa; /* failing CCW */ if (from < fail - 2) { from = fail - 2; /* there is a gap - print header */ len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n"); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 116611583df..aee4656127f 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -222,7 +222,7 @@ static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, /* first of all check for state change pending interrupt */ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP; - if ((irb->scsw.dstat & mask) == mask) { + if ((irb->scsw.cmd.dstat & mask) == mask) { dasd_generic_handle_state_change(device); return; } @@ -449,11 +449,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, device->cdev->dev.bus_id); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " in req: %p CS: 0x%02X DS: 0x%02X\n", req, - irb->scsw.cstat, irb->scsw.dstat); + irb->scsw.cmd.cstat, irb->scsw.cmd.dstat); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing CCW: %p\n", device->cdev->dev.bus_id, - (void *) (addr_t) irb->scsw.cpa); + (void *) (addr_t) irb->scsw.cmd.cpa); if (irb->esw.esw0.erw.cons) { for (sl = 0; sl < 4; sl++) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER @@ -498,11 +498,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req, /* print failing CCW area */ len = 0; - if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) { - act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2; + if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) { + act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2; len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n"); } - end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last); + end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last); while (act <= end) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER " CCW %p: %08X %08X DAT:", diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index bb52d2fbac1..01fcdd91b84 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -167,10 +167,8 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch struct dcssblk_dev_info *dev_info; int rc; - if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { - PRINT_WARN("Invalid value, must be 0 or 1\n"); + if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) return -EINVAL; - } down_write(&dcssblk_devices_sem); dev_info = container_of(dev, struct dcssblk_dev_info, dev); if (atomic_read(&dev_info->use_count)) { @@ -215,7 +213,6 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch set_disk_ro(dev_info->gd, 0); } } else { - PRINT_WARN("Invalid value, must be 0 or 1\n"); rc = -EINVAL; goto out; } @@ -258,10 +255,8 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char { struct dcssblk_dev_info *dev_info; - if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { - PRINT_WARN("Invalid value, must be 0 or 1\n"); + if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) return -EINVAL; - } dev_info = container_of(dev, struct dcssblk_dev_info, dev); down_write(&dcssblk_devices_sem); @@ -289,7 +284,6 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char } } else { up_write(&dcssblk_devices_sem); - PRINT_WARN("Invalid value, must be 0 or 1\n"); return -EINVAL; } up_write(&dcssblk_devices_sem); @@ -441,7 +435,6 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char goto out; unregister_dev: - PRINT_ERR("device_create_file() failed!\n"); list_del(&dev_info->lh); blk_cleanup_queue(dev_info->dcssblk_queue); dev_info->gd->queue = NULL; @@ -702,10 +695,8 @@ dcssblk_check_params(void) static void __exit dcssblk_exit(void) { - PRINT_DEBUG("DCSSBLOCK EXIT...\n"); s390_root_dev_unregister(dcssblk_root_dev); unregister_blkdev(dcssblk_major, DCSSBLK_NAME); - PRINT_DEBUG("...finished!\n"); } static int __init @@ -713,27 +704,21 @@ dcssblk_init(void) { int rc; - PRINT_DEBUG("DCSSBLOCK INIT...\n"); dcssblk_root_dev = s390_root_dev_register("dcssblk"); - if (IS_ERR(dcssblk_root_dev)) { - PRINT_ERR("device_register() failed!\n"); + if (IS_ERR(dcssblk_root_dev)) return PTR_ERR(dcssblk_root_dev); - } rc = device_create_file(dcssblk_root_dev, &dev_attr_add); if (rc) { - PRINT_ERR("device_create_file(add) failed!\n"); s390_root_dev_unregister(dcssblk_root_dev); return rc; } rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); if (rc) { - PRINT_ERR("device_create_file(remove) failed!\n"); s390_root_dev_unregister(dcssblk_root_dev); return rc; } rc = register_blkdev(0, DCSSBLK_NAME); if (rc < 0) { - PRINT_ERR("Can't get dynamic major!\n"); s390_root_dev_unregister(dcssblk_root_dev); return rc; } @@ -742,7 +727,6 @@ dcssblk_init(void) dcssblk_check_params(); - PRINT_DEBUG("...finished!\n"); return 0; } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index f231bc21b1c..dd9b986389a 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -100,15 +100,10 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index) : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); if (cc == 3) return -ENXIO; - if (cc == 2) { - PRINT_ERR("expanded storage lost!\n"); + if (cc == 2) return -ENXIO; - } - if (cc == 1) { - PRINT_ERR("page in failed for page index %u.\n", - xpage_index); + if (cc == 1) return -EIO; - } return 0; } @@ -135,15 +130,10 @@ static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index) : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc"); if (cc == 3) return -ENXIO; - if (cc == 2) { - PRINT_ERR("expanded storage lost!\n"); + if (cc == 2) return -ENXIO; - } - if (cc == 1) { - PRINT_ERR("page out failed for page index %u.\n", - xpage_index); + if (cc == 1) return -EIO; - } return 0; } diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 3e5653c92f4..d3ec9b55ab3 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -93,9 +93,6 @@ struct raw3215_info { struct raw3215_req *queued_write;/* pointer to queued write requests */ wait_queue_head_t empty_wait; /* wait queue for flushing */ struct timer_list timer; /* timer for delayed output */ - char *message; /* pending message from raw3215_irq */ - int msg_dstat; /* dstat for pending message */ - int msg_cstat; /* cstat for pending message */ int line_pos; /* position on the line (for tabs) */ char ubuffer[80]; /* copy_from_user buffer */ }; @@ -359,11 +356,6 @@ raw3215_tasklet(void *data) raw3215_mk_write_req(raw); raw3215_try_io(raw); spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); - /* Check for pending message from raw3215_irq */ - if (raw->message != NULL) { - printk(raw->message, raw->msg_dstat, raw->msg_cstat); - raw->message = NULL; - } tty = raw->tty; if (tty != NULL && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { @@ -381,20 +373,14 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct raw3215_req *req; struct tty_struct *tty; int cstat, dstat; - int count, slen; + int count; raw = cdev->dev.driver_data; req = (struct raw3215_req *) intparm; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; - if (cstat != 0) { - raw->message = KERN_WARNING - "Got nonzero channel status in raw3215_irq " - "(dev sts 0x%2x, sch sts 0x%2x)"; - raw->msg_dstat = dstat; - raw->msg_cstat = cstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; + if (cstat != 0) tasklet_schedule(&raw->tasklet); - } if (dstat & 0x01) { /* we got a unit exception */ dstat &= ~0x01; /* we can ignore it */ } @@ -404,8 +390,6 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) break; /* Attention interrupt, someone hit the enter key */ raw3215_mk_read_req(raw); - if (MACHINE_IS_P390) - memset(raw->inbuf, 0, RAW3215_INBUF_SIZE); tasklet_schedule(&raw->tasklet); break; case 0x08: @@ -415,7 +399,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) return; /* That shouldn't happen ... */ if (req->type == RAW3215_READ) { /* store residual count, then wait for device end */ - req->residual = irb->scsw.count; + req->residual = irb->scsw.cmd.count; } if (dstat == 0x08) break; @@ -428,11 +412,6 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) tty = raw->tty; count = 160 - req->residual; - if (MACHINE_IS_P390) { - slen = strnlen(raw->inbuf, RAW3215_INBUF_SIZE); - if (count > slen) - count = slen; - } else EBCASC(raw->inbuf, count); cchar = ctrlchar_handle(raw->inbuf, count, tty); switch (cchar & CTRLCHAR_MASK) { @@ -481,11 +460,6 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) raw->flags &= ~RAW3215_WORKING; raw3215_free_req(req); } - raw->message = KERN_WARNING - "Spurious interrupt in in raw3215_irq " - "(dev sts 0x%2x, sch sts 0x%2x)"; - raw->msg_dstat = dstat; - raw->msg_cstat = cstat; tasklet_schedule(&raw->tasklet); } return; @@ -883,7 +857,6 @@ con3215_init(void) free_bootmem((unsigned long) raw->buffer, RAW3215_BUFFER_SIZE); free_bootmem((unsigned long) raw, sizeof(struct raw3215_info)); raw3215[0] = NULL; - printk("Couldn't find a 3215 console device\n"); return -ENODEV; } register_console(&con3215); @@ -1157,7 +1130,6 @@ tty3215_init(void) tty_set_operations(driver, &tty3215_ops); ret = tty_register_driver(driver); if (ret) { - printk("Couldn't register tty3215 driver\n"); put_tty_driver(driver); return ret; } diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 0b040557db0..3c07974886e 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -411,15 +411,15 @@ static int con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ - if (irb->scsw.dstat & DEV_STAT_ATTENTION) + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) con3270_issue_read(cp); if (rq) { - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) rq->rc = -EIO; else /* Normal end. Copy residual count. */ - rq->rescnt = irb->scsw.count; + rq->rescnt = irb->scsw.cmd.count; } return RAW3270_IO_DONE; } diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index ef36f2132aa..e136d10a0de 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -216,17 +216,17 @@ static int fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Set indication and wake waiters for attention. */ - if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { fp->attention = 1; wake_up(&fp->wait); } if (rq) { - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) rq->rc = -EIO; else /* Normal end. Copy residual count. */ - rq->rescnt = irb->scsw.count; + rq->rescnt = irb->scsw.cmd.count; } return RAW3270_IO_DONE; } @@ -512,11 +512,8 @@ fs3270_init(void) int rc; rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); - if (rc) { - printk(KERN_ERR "fs3270 can't get major number %d: errno %d\n", - IBM_FS3270_MAJOR, rc); + if (rc) return rc; - } return 0; } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 1e1f50655bb..f0e4c96afbf 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -3,9 +3,8 @@ * * Character device driver for reading z/VM *MONITOR service records. * - * Copyright 2004 IBM Corporation, IBM Deutschland Entwicklung GmbH. - * - * Author: Gerald Schaefer <geraldsc@de.ibm.com> + * Copyright IBM Corp. 2004, 2008 + * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> */ #include <linux/module.h> @@ -18,12 +17,11 @@ #include <linux/ctype.h> #include <linux/spinlock.h> #include <linux/interrupt.h> +#include <linux/poll.h> +#include <net/iucv/iucv.h> #include <asm/uaccess.h> #include <asm/ebcdic.h> #include <asm/extmem.h> -#include <linux/poll.h> -#include <net/iucv/iucv.h> - //#define MON_DEBUG /* Debug messages on/off */ @@ -152,10 +150,7 @@ static int mon_check_mca(struct mon_msg *monmsg) (mon_mca_end(monmsg) > mon_dcss_end) || (mon_mca_start(monmsg) < mon_dcss_start) || ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0))) - { - P_DEBUG("READ, IGNORED INVALID MCA\n\n"); return -EINVAL; - } return 0; } @@ -164,10 +159,6 @@ static int mon_send_reply(struct mon_msg *monmsg, { int rc; - P_DEBUG("read, REPLY: pathid = 0x%04X, msgid = 0x%08X, trgcls = " - "0x%08X\n\n", - monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class); - rc = iucv_message_reply(monpriv->path, &monmsg->msg, IUCV_IPRMDATA, NULL, 0); atomic_dec(&monpriv->msglim_count); @@ -202,15 +193,12 @@ static struct mon_private *mon_alloc_mem(void) struct mon_private *monpriv; monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); - if (!monpriv) { - P_ERROR("no memory for monpriv\n"); + if (!monpriv) return NULL; - } for (i = 0; i < MON_MSGLIM; i++) { monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg), GFP_KERNEL); if (!monpriv->msg_array[i]) { - P_ERROR("open, no memory for msg_array\n"); mon_free_mem(monpriv); return NULL; } @@ -218,41 +206,10 @@ static struct mon_private *mon_alloc_mem(void) return monpriv; } -static inline void mon_read_debug(struct mon_msg *monmsg, - struct mon_private *monpriv) -{ -#ifdef MON_DEBUG - u8 msg_type[2], mca_type; - unsigned long records_len; - - records_len = mon_rec_end(monmsg) - mon_rec_start(monmsg) + 1; - - memcpy(msg_type, &monmsg->msg.class, 2); - EBCASC(msg_type, 2); - mca_type = mon_mca_type(monmsg, 0); - EBCASC(&mca_type, 1); - - P_DEBUG("read, mon_read_index = %i, mon_write_index = %i\n", - monpriv->read_index, monpriv->write_index); - P_DEBUG("read, pathid = 0x%04X, msgid = 0x%08X, trgcls = 0x%08X\n", - monpriv->path->pathid, monmsg->msg.id, monmsg->msg.class); - P_DEBUG("read, msg_type = '%c%c', mca_type = '%c' / 0x%X / 0x%X\n", - msg_type[0], msg_type[1], mca_type ? mca_type : 'X', - mon_mca_type(monmsg, 1), mon_mca_type(monmsg, 2)); - P_DEBUG("read, MCA: start = 0x%lX, end = 0x%lX\n", - mon_mca_start(monmsg), mon_mca_end(monmsg)); - P_DEBUG("read, REC: start = 0x%X, end = 0x%X, len = %lu\n\n", - mon_rec_start(monmsg), mon_rec_end(monmsg), records_len); - if (mon_mca_size(monmsg) > 12) - P_DEBUG("READ, MORE THAN ONE MCA\n\n"); -#endif -} - static inline void mon_next_mca(struct mon_msg *monmsg) { if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12)) return; - P_DEBUG("READ, NEXT MCA\n\n"); monmsg->mca_offset += 12; monmsg->pos = 0; } @@ -269,7 +226,6 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv) monmsg->msglim_reached = 0; monmsg->pos = 0; monmsg->mca_offset = 0; - P_WARNING("read, message limit reached\n"); monpriv->read_index = (monpriv->read_index + 1) % MON_MSGLIM; atomic_dec(&monpriv->read_ready); @@ -286,10 +242,6 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16]) { struct mon_private *monpriv = path->private; - P_DEBUG("IUCV connection completed\n"); - P_DEBUG("IUCV ACCEPT (from *MONITOR): Version = 0x%02X, Event = " - "0x%02X, Sample = 0x%02X\n", - ipuser[0], ipuser[1], ipuser[2]); atomic_set(&monpriv->iucv_connected, 1); wake_up(&mon_conn_wait_queue); } @@ -310,7 +262,6 @@ static void mon_iucv_message_pending(struct iucv_path *path, { struct mon_private *monpriv = path->private; - P_DEBUG("IUCV message pending\n"); memcpy(&monpriv->msg_array[monpriv->write_index]->msg, msg, sizeof(*msg)); if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) { @@ -375,7 +326,6 @@ static int mon_open(struct inode *inode, struct file *filp) rc = -EIO; goto out_path; } - P_INFO("open, established connection to *MONITOR service\n\n"); filp->private_data = monpriv; return nonseekable_open(inode, filp); @@ -400,8 +350,6 @@ static int mon_close(struct inode *inode, struct file *filp) rc = iucv_path_sever(monpriv->path, user_data_sever); if (rc) P_ERROR("close, iucv_sever failed with rc = %i\n", rc); - else - P_INFO("close, terminated connection to *MONITOR service\n"); atomic_set(&monpriv->iucv_severed, 0); atomic_set(&monpriv->iucv_connected, 0); @@ -442,10 +390,8 @@ static ssize_t mon_read(struct file *filp, char __user *data, monmsg = monpriv->msg_array[monpriv->read_index]; } - if (!monmsg->pos) { + if (!monmsg->pos) monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset; - mon_read_debug(monmsg, monpriv); - } if (mon_check_mca(monmsg)) goto reply; @@ -531,7 +477,6 @@ static int __init mon_init(void) P_ERROR("failed to register with iucv driver\n"); return rc; } - P_INFO("open, registered with IUCV\n"); rc = segment_type(mon_dcss_name); if (rc < 0) { @@ -555,13 +500,8 @@ static int __init mon_init(void) dcss_mkname(mon_dcss_name, &user_data_connect[8]); rc = misc_register(&mon_dev); - if (rc < 0 ) { - P_ERROR("misc_register failed, rc = %i\n", rc); + if (rc < 0 ) goto out; - } - P_INFO("Loaded segment %s from %p to %p, size = %lu Byte\n", - mon_dcss_name, (void *) mon_dcss_start, (void *) mon_dcss_end, - mon_dcss_end - mon_dcss_start + 1); return 0; out: diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 848ef7e8523..81a96e01908 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -153,19 +153,10 @@ struct raw3270_request __init *raw3270_request_alloc_bootmem(size_t size) struct raw3270_request *rq; rq = alloc_bootmem_low(sizeof(struct raw3270)); - if (!rq) - return ERR_PTR(-ENOMEM); - memset(rq, 0, sizeof(struct raw3270_request)); /* alloc output buffer. */ - if (size > 0) { + if (size > 0) rq->buffer = alloc_bootmem_low(size); - if (!rq->buffer) { - free_bootmem((unsigned long) rq, - sizeof(struct raw3270)); - return ERR_PTR(-ENOMEM); - } - } rq->size = size; INIT_LIST_HEAD(&rq->list); @@ -372,17 +363,17 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) if (IS_ERR(irb)) rc = RAW3270_IO_RETRY; - else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { + else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { rq->rc = -EIO; rc = RAW3270_IO_DONE; - } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | - DEV_STAT_UNIT_EXCEP)) { + } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END | + DEV_STAT_UNIT_EXCEP)) { /* Handle CE-DE-UE and subsequent UDE */ set_bit(RAW3270_FLAGS_BUSY, &rp->flags); rc = RAW3270_IO_BUSY; } else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) { /* Wait for UDE if busy flag is set. */ - if (irb->scsw.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { clear_bit(RAW3270_FLAGS_BUSY, &rp->flags); /* Got it, now retry. */ rc = RAW3270_IO_RETRY; @@ -497,7 +488,7 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, * Unit-Check Processing: * Expect Command Reject or Intervention Required. */ - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { /* Request finished abnormally. */ if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); @@ -505,16 +496,16 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, } } if (rq) { - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { if (irb->ecw[0] & SNS0_CMD_REJECT) rq->rc = -EOPNOTSUPP; else rq->rc = -EIO; } else /* Request finished normally. Copy residual count. */ - rq->rescnt = irb->scsw.count; + rq->rescnt = irb->scsw.cmd.count; } - if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); wake_up(&raw3270_wait_queue); } @@ -619,7 +610,6 @@ __raw3270_size_device_vm(struct raw3270 *rp) rp->cols = 132; break; default: - printk(KERN_WARNING "vrdccrmd is 0x%.8x\n", model); rc = -EOPNOTSUPP; break; } diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 2c7a1ee6b04..3c8b25e6c34 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -506,6 +506,8 @@ sclp_state_change_cb(struct evbuf_header *evbuf) if (scbuf->validity_sclp_send_mask) sclp_send_mask = scbuf->sclp_send_mask; spin_unlock_irqrestore(&sclp_lock, flags); + if (scbuf->validity_sclp_active_facility_mask) + sclp_facilities = scbuf->sclp_active_facility_mask; sclp_dispatch_state_change(); } @@ -782,11 +784,9 @@ sclp_check_handler(__u16 code) /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) return; - if (finished_sccb != (u32) (addr_t) sclp_init_sccb) { - printk(KERN_WARNING SCLP_HEADER "unsolicited interrupt " - "for buffer at 0x%x\n", finished_sccb); - return; - } + if (finished_sccb != (u32) (addr_t) sclp_init_sccb) + panic("sclp: unsolicited interrupt for buffer at 0x%x\n", + finished_sccb); spin_lock(&sclp_lock); if (sclp_running_state == sclp_running_state_running) { sclp_init_req.status = SCLP_REQ_DONE; @@ -883,8 +883,6 @@ sclp_init(void) unsigned long flags; int rc; - if (!MACHINE_HAS_SCLP) - return -ENODEV; spin_lock_irqsave(&sclp_lock, flags); /* Check for previous or running initialization */ if (sclp_init_state != sclp_init_state_uninitialized) { diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index b5c23396f8f..0c2b77493db 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -11,6 +11,9 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/mm.h> +#include <linux/mmzone.h> +#include <linux/memory.h> #include <asm/chpid.h> #include <asm/sclp.h> #include "sclp.h" @@ -43,6 +46,8 @@ static int __initdata early_read_info_sccb_valid; u64 sclp_facilities; static u8 sclp_fac84; +static unsigned long long rzm; +static unsigned long long rnmax; static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) { @@ -62,7 +67,7 @@ out: return rc; } -void __init sclp_read_info_early(void) +static void __init sclp_read_info_early(void) { int rc; int i; @@ -92,34 +97,33 @@ void __init sclp_read_info_early(void) void __init sclp_facilities_detect(void) { + struct read_info_sccb *sccb; + + sclp_read_info_early(); if (!early_read_info_sccb_valid) return; - sclp_facilities = early_read_info_sccb.facilities; - sclp_fac84 = early_read_info_sccb.fac84; + + sccb = &early_read_info_sccb; + sclp_facilities = sccb->facilities; + sclp_fac84 = sccb->fac84; + rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; + rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; + rzm <<= 20; } -unsigned long long __init sclp_memory_detect(void) +unsigned long long sclp_get_rnmax(void) { - unsigned long long memsize; - struct read_info_sccb *sccb; + return rnmax; +} - if (!early_read_info_sccb_valid) - return 0; - sccb = &early_read_info_sccb; - if (sccb->rnsize) - memsize = sccb->rnsize << 20; - else - memsize = sccb->rnsize2 << 20; - if (sccb->rnmax) - memsize *= sccb->rnmax; - else - memsize *= sccb->rnmax2; - return memsize; +unsigned long long sclp_get_rzm(void) +{ + return rzm; } /* - * This function will be called after sclp_memory_detect(), which gets called - * early from early.c code. Therefore the sccb should have valid contents. + * This function will be called after sclp_facilities_detect(), which gets + * called from early.c code. Therefore the sccb should have valid contents. */ void __init sclp_get_ipl_info(struct sclp_ipl_info *info) { @@ -278,6 +282,305 @@ int sclp_cpu_deconfigure(u8 cpu) return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8); } +#ifdef CONFIG_MEMORY_HOTPLUG + +static DEFINE_MUTEX(sclp_mem_mutex); +static LIST_HEAD(sclp_mem_list); +static u8 sclp_max_storage_id; +static unsigned long sclp_storage_ids[256 / BITS_PER_LONG]; + +struct memory_increment { + struct list_head list; + u16 rn; + int standby; + int usecount; +}; + +struct assign_storage_sccb { + struct sccb_header header; + u16 rn; +} __packed; + +static unsigned long long rn2addr(u16 rn) +{ + return (unsigned long long) (rn - 1) * rzm; +} + +static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) +{ + struct assign_storage_sccb *sccb; + int rc; + + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = PAGE_SIZE; + sccb->rn = rn; + rc = do_sync_request(cmd, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + case 0x0120: + break; + default: + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +static int sclp_assign_storage(u16 rn) +{ + return do_assign_storage(0x000d0001, rn); +} + +static int sclp_unassign_storage(u16 rn) +{ + return do_assign_storage(0x000c0001, rn); +} + +struct attach_storage_sccb { + struct sccb_header header; + u16 :16; + u16 assigned; + u32 :32; + u32 entries[0]; +} __packed; + +static int sclp_attach_storage(u8 id) +{ + struct attach_storage_sccb *sccb; + int rc; + int i; + + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + sccb->header.length = PAGE_SIZE; + rc = do_sync_request(0x00080001 | id << 8, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0020: + set_bit(id, sclp_storage_ids); + for (i = 0; i < sccb->assigned; i++) + sclp_unassign_storage(sccb->entries[i] >> 16); + break; + default: + rc = -EIO; + break; + } +out: + free_page((unsigned long) sccb); + return rc; +} + +static int sclp_mem_change_state(unsigned long start, unsigned long size, + int online) +{ + struct memory_increment *incr; + unsigned long long istart; + int rc = 0; + + list_for_each_entry(incr, &sclp_mem_list, list) { + istart = rn2addr(incr->rn); + if (start + size - 1 < istart) + break; + if (start > istart + rzm - 1) + continue; + if (online) { + if (incr->usecount++) + continue; + /* + * Don't break the loop if one assign fails. Loop may + * be walked again on CANCEL and we can't save + * information if state changed before or not. + * So continue and increase usecount for all increments. + */ + rc |= sclp_assign_storage(incr->rn); + } else { + if (--incr->usecount) + continue; + sclp_unassign_storage(incr->rn); + } + } + return rc ? -EIO : 0; +} + +static int sclp_mem_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + unsigned long start, size; + struct memory_notify *arg; + unsigned char id; + int rc = 0; + + arg = data; + start = arg->start_pfn << PAGE_SHIFT; + size = arg->nr_pages << PAGE_SHIFT; + mutex_lock(&sclp_mem_mutex); + for (id = 0; id <= sclp_max_storage_id; id++) + if (!test_bit(id, sclp_storage_ids)) + sclp_attach_storage(id); + switch (action) { + case MEM_ONLINE: + break; + case MEM_GOING_ONLINE: + rc = sclp_mem_change_state(start, size, 1); + break; + case MEM_CANCEL_ONLINE: + sclp_mem_change_state(start, size, 0); + break; + default: + rc = -EINVAL; + break; + } + mutex_unlock(&sclp_mem_mutex); + return rc ? NOTIFY_BAD : NOTIFY_OK; +} + +static struct notifier_block sclp_mem_nb = { + .notifier_call = sclp_mem_notifier, +}; + +static void __init add_memory_merged(u16 rn) +{ + static u16 first_rn, num; + unsigned long long start, size; + + if (rn && first_rn && (first_rn + num == rn)) { + num++; + return; + } + if (!first_rn) + goto skip_add; + start = rn2addr(first_rn); + size = (unsigned long long ) num * rzm; + if (start >= VMEM_MAX_PHYS) + goto skip_add; + if (start + size > VMEM_MAX_PHYS) + size = VMEM_MAX_PHYS - start; + add_memory(0, start, size); +skip_add: + first_rn = rn; + num = 1; +} + +static void __init sclp_add_standby_memory(void) +{ + struct memory_increment *incr; + + list_for_each_entry(incr, &sclp_mem_list, list) + if (incr->standby) + add_memory_merged(incr->rn); + add_memory_merged(0); +} + +static void __init insert_increment(u16 rn, int standby, int assigned) +{ + struct memory_increment *incr, *new_incr; + struct list_head *prev; + u16 last_rn; + + new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL); + if (!new_incr) + return; + new_incr->rn = rn; + new_incr->standby = standby; + last_rn = 0; + prev = &sclp_mem_list; + list_for_each_entry(incr, &sclp_mem_list, list) { + if (assigned && incr->rn > rn) + break; + if (!assigned && incr->rn - last_rn > 1) + break; + last_rn = incr->rn; + prev = &incr->list; + } + if (!assigned) + new_incr->rn = last_rn + 1; + if (new_incr->rn > rnmax) { + kfree(new_incr); + return; + } + list_add(&new_incr->list, prev); +} + +struct read_storage_sccb { + struct sccb_header header; + u16 max_id; + u16 assigned; + u16 standby; + u16 :16; + u32 entries[0]; +} __packed; + +static int __init sclp_detect_standby_memory(void) +{ + struct read_storage_sccb *sccb; + int i, id, assigned, rc; + + if (!early_read_info_sccb_valid) + return 0; + if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL) + return 0; + rc = -ENOMEM; + sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + goto out; + assigned = 0; + for (id = 0; id <= sclp_max_storage_id; id++) { + memset(sccb, 0, PAGE_SIZE); + sccb->header.length = PAGE_SIZE; + rc = do_sync_request(0x00040001 | id << 8, sccb); + if (rc) + goto out; + switch (sccb->header.response_code) { + case 0x0010: + set_bit(id, sclp_storage_ids); + for (i = 0; i < sccb->assigned; i++) { + if (!sccb->entries[i]) + continue; + assigned++; + insert_increment(sccb->entries[i] >> 16, 0, 1); + } + break; + case 0x0310: + break; + case 0x0410: + for (i = 0; i < sccb->assigned; i++) { + if (!sccb->entries[i]) + continue; + assigned++; + insert_increment(sccb->entries[i] >> 16, 1, 1); + } + break; + default: + rc = -EIO; + break; + } + if (!rc) + sclp_max_storage_id = sccb->max_id; + } + if (rc || list_empty(&sclp_mem_list)) + goto out; + for (i = 1; i <= rnmax - assigned; i++) + insert_increment(0, 1, 0); + rc = register_memory_notifier(&sclp_mem_nb); + if (rc) + goto out; + sclp_add_standby_memory(); +out: + free_page((unsigned long) sccb); + return rc; +} +__initcall(sclp_detect_standby_memory); + +#endif /* CONFIG_MEMORY_HOTPLUG */ + /* * Channel path configuration related functions. */ diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index ead1043d788..7e619c534bf 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -14,14 +14,13 @@ #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/bootmem.h> +#include <linux/termios.h> #include <linux/err.h> #include "sclp.h" #include "sclp_rw.h" #include "sclp_tty.h" -#define SCLP_CON_PRINT_HEADER "sclp console driver: " - #define sclp_console_major 4 /* TTYAUX_MAJOR */ #define sclp_console_minor 64 #define sclp_console_name "ttyS" @@ -222,8 +221,6 @@ sclp_console_init(void) INIT_LIST_HEAD(&sclp_con_pages); for (i = 0; i < MAX_CONSOLE_PAGES; i++) { page = alloc_bootmem_low_pages(PAGE_SIZE); - if (page == NULL) - return -ENOMEM; list_add_tail((struct list_head *) page, &sclp_con_pages); } INIT_LIST_HEAD(&sclp_con_outqueue); diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index ad05a87bc48..fff4ff485d9 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -8,6 +8,7 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/cpu.h> +#include <linux/kthread.h> #include <linux/sysdev.h> #include <linux/workqueue.h> #include <asm/smp.h> @@ -40,9 +41,19 @@ static void sclp_cpu_capability_notify(struct work_struct *work) put_online_cpus(); } -static void __ref sclp_cpu_change_notify(struct work_struct *work) +static int sclp_cpu_kthread(void *data) { smp_rescan_cpus(); + return 0; +} + +static void __ref sclp_cpu_change_notify(struct work_struct *work) +{ + /* Can't call smp_rescan_cpus() from workqueue context since it may + * deadlock in case of cpu hotplug. So we have to create a kernel + * thread in order to call it. + */ + kthread_run(sclp_cpu_kthread, NULL, "cpu_rescan"); } static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) @@ -74,10 +85,8 @@ static int __init sclp_conf_init(void) INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); rc = sclp_register(&sclp_conf_register); - if (rc) { - printk(KERN_ERR TAG "failed to register (%d).\n", rc); + if (rc) return rc; - } if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) { printk(KERN_WARNING TAG "no configuration management.\n"); diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index 9f37456222e..d887bd261d2 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -27,6 +27,8 @@ #define CPI_LENGTH_NAME 8 #define CPI_LENGTH_LEVEL 16 +static DEFINE_MUTEX(sclp_cpi_mutex); + struct cpi_evbuf { struct evbuf_header header; u8 id_format; @@ -124,21 +126,15 @@ static int cpi_req(void) int response; rc = sclp_register(&sclp_cpi_event); - if (rc) { - printk(KERN_WARNING "cpi: could not register " - "to hardware console.\n"); + if (rc) goto out; - } if (!(sclp_cpi_event.sclp_receive_mask & EVTYP_CTLPROGIDENT_MASK)) { - printk(KERN_WARNING "cpi: no control program " - "identification support\n"); rc = -EOPNOTSUPP; goto out_unregister; } req = cpi_prepare_req(); if (IS_ERR(req)) { - printk(KERN_WARNING "cpi: could not allocate request\n"); rc = PTR_ERR(req); goto out_unregister; } @@ -148,10 +144,8 @@ static int cpi_req(void) /* Add request to sclp queue */ rc = sclp_add_request(req); - if (rc) { - printk(KERN_WARNING "cpi: could not start request\n"); + if (rc) goto out_free_req; - } wait_for_completion(&completion); @@ -223,7 +217,12 @@ static void set_string(char *attr, const char *value) static ssize_t system_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { - return snprintf(page, PAGE_SIZE, "%s\n", system_name); + int rc; + + mutex_lock(&sclp_cpi_mutex); + rc = snprintf(page, PAGE_SIZE, "%s\n", system_name); + mutex_unlock(&sclp_cpi_mutex); + return rc; } static ssize_t system_name_store(struct kobject *kobj, @@ -237,7 +236,9 @@ static ssize_t system_name_store(struct kobject *kobj, if (rc) return rc; + mutex_lock(&sclp_cpi_mutex); set_string(system_name, buf); + mutex_unlock(&sclp_cpi_mutex); return len; } @@ -248,7 +249,12 @@ static struct kobj_attribute system_name_attr = static ssize_t sysplex_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { - return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name); + int rc; + + mutex_lock(&sclp_cpi_mutex); + rc = snprintf(page, PAGE_SIZE, "%s\n", sysplex_name); + mutex_unlock(&sclp_cpi_mutex); + return rc; } static ssize_t sysplex_name_store(struct kobject *kobj, @@ -262,7 +268,9 @@ static ssize_t sysplex_name_store(struct kobject *kobj, if (rc) return rc; + mutex_lock(&sclp_cpi_mutex); set_string(sysplex_name, buf); + mutex_unlock(&sclp_cpi_mutex); return len; } @@ -273,7 +281,12 @@ static struct kobj_attribute sysplex_name_attr = static ssize_t system_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { - return snprintf(page, PAGE_SIZE, "%s\n", system_type); + int rc; + + mutex_lock(&sclp_cpi_mutex); + rc = snprintf(page, PAGE_SIZE, "%s\n", system_type); + mutex_unlock(&sclp_cpi_mutex); + return rc; } static ssize_t system_type_store(struct kobject *kobj, @@ -287,7 +300,9 @@ static ssize_t system_type_store(struct kobject *kobj, if (rc) return rc; + mutex_lock(&sclp_cpi_mutex); set_string(system_type, buf); + mutex_unlock(&sclp_cpi_mutex); return len; } @@ -298,8 +313,11 @@ static struct kobj_attribute system_type_attr = static ssize_t system_level_show(struct kobject *kobj, struct kobj_attribute *attr, char *page) { - unsigned long long level = system_level; + unsigned long long level; + mutex_lock(&sclp_cpi_mutex); + level = system_level; + mutex_unlock(&sclp_cpi_mutex); return snprintf(page, PAGE_SIZE, "%#018llx\n", level); } @@ -320,8 +338,9 @@ static ssize_t system_level_store(struct kobject *kobj, if (*endp) return -EINVAL; + mutex_lock(&sclp_cpi_mutex); system_level = level; - + mutex_unlock(&sclp_cpi_mutex); return len; } @@ -334,7 +353,9 @@ static ssize_t set_store(struct kobject *kobj, { int rc; + mutex_lock(&sclp_cpi_mutex); rc = cpi_req(); + mutex_unlock(&sclp_cpi_mutex); if (rc) return rc; @@ -373,12 +394,16 @@ int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type, if (rc) return rc; + mutex_lock(&sclp_cpi_mutex); set_string(system_name, system); set_string(sysplex_name, sysplex); set_string(system_type, type); system_level = level; - return cpi_req(); + rc = cpi_req(); + mutex_unlock(&sclp_cpi_mutex); + + return rc; } EXPORT_SYMBOL(sclp_cpi_set_data); diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index 45ff25e787c..84c191c1cd6 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -51,13 +51,7 @@ static struct sclp_register sclp_quiesce_event = { static int __init sclp_quiesce_init(void) { - int rc; - - rc = sclp_register(&sclp_quiesce_event); - if (rc) - printk(KERN_WARNING "sclp: could not register quiesce handler " - "(rc=%d)\n", rc); - return rc; + return sclp_register(&sclp_quiesce_event); } module_init(sclp_quiesce_init); diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index da09781b32f..710af42603f 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -19,8 +19,6 @@ #include "sclp.h" #include "sclp_rw.h" -#define SCLP_RW_PRINT_HEADER "sclp low level driver: " - /* * The room for the SCCB (only for writing) is not equal to a pages size * (as it is specified as the maximum size in the SCLP documentation) diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index 1c064976b32..8b854857ba0 100644 --- a/drivers/s390/char/sclp_sdias.c +++ b/drivers/s390/char/sclp_sdias.c @@ -239,10 +239,8 @@ int __init sclp_sdias_init(void) debug_register_view(sdias_dbf, &debug_sprintf_view); debug_set_level(sdias_dbf, 6); rc = sclp_register(&sclp_sdias_register); - if (rc) { - ERROR_MSG("sclp register failed\n"); + if (rc) return rc; - } init_waitqueue_head(&sdias_wq); TRACE("init done\n"); return 0; diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 40b11521cd2..434ba04b130 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -13,7 +13,6 @@ #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> -#include <linux/wait.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/init.h> @@ -25,8 +24,6 @@ #include "sclp_rw.h" #include "sclp_tty.h" -#define SCLP_TTY_PRINT_HEADER "sclp tty driver: " - /* * size of a buffer that collects single characters coming in * via sclp_tty_put_char() @@ -50,8 +47,6 @@ static int sclp_tty_buffer_count; static struct sclp_buffer *sclp_ttybuf; /* Timer for delayed output of console messages. */ static struct timer_list sclp_tty_timer; -/* Waitqueue to wait for buffers to get empty. */ -static wait_queue_head_t sclp_tty_waitq; static struct tty_struct *sclp_tty; static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE]; @@ -59,19 +54,11 @@ static unsigned short int sclp_tty_chars_count; struct tty_driver *sclp_tty_driver; -static struct sclp_ioctls sclp_ioctls; -static struct sclp_ioctls sclp_ioctls_init = -{ - 8, /* 1 hor. tab. = 8 spaces */ - 0, /* no echo of input by this driver */ - 80, /* 80 characters/line */ - 1, /* write after 1/10 s without final new line */ - MAX_KMEM_PAGES, /* quick fix: avoid __alloc_pages */ - MAX_KMEM_PAGES, /* take 32/64 pages from kernel memory, */ - 0, /* do not convert to lower case */ - 0x6c /* to seprate upper and lower case */ - /* ('%' in EBCDIC) */ -}; +static int sclp_tty_tolower; +static int sclp_tty_columns = 80; + +#define SPACES_PER_TAB 8 +#define CASE_DELIMITER 0x6c /* to separate upper and lower case (% in EBCDIC) */ /* This routine is called whenever we try to open a SCLP terminal. */ static int @@ -92,136 +79,6 @@ sclp_tty_close(struct tty_struct *tty, struct file *filp) sclp_tty = NULL; } -/* execute commands to control the i/o behaviour of the SCLP tty at runtime */ -static int -sclp_tty_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - unsigned int obuf; - int check; - int rc; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - rc = 0; - check = 0; - switch (cmd) { - case TIOCSCLPSHTAB: - /* set width of horizontal tab */ - if (get_user(sclp_ioctls.htab, (unsigned short __user *) arg)) - rc = -EFAULT; - else - check = 1; - break; - case TIOCSCLPGHTAB: - /* get width of horizontal tab */ - if (put_user(sclp_ioctls.htab, (unsigned short __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSECHO: - /* enable/disable echo of input */ - if (get_user(sclp_ioctls.echo, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPGECHO: - /* Is echo of input enabled ? */ - if (put_user(sclp_ioctls.echo, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSCOLS: - /* set number of columns for output */ - if (get_user(sclp_ioctls.columns, (unsigned short __user *) arg)) - rc = -EFAULT; - else - check = 1; - break; - case TIOCSCLPGCOLS: - /* get number of columns for output */ - if (put_user(sclp_ioctls.columns, (unsigned short __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSNL: - /* enable/disable writing without final new line character */ - if (get_user(sclp_ioctls.final_nl, (signed char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPGNL: - /* Is writing without final new line character enabled ? */ - if (put_user(sclp_ioctls.final_nl, (signed char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSOBUF: - /* - * set the maximum buffers size for output, will be rounded - * up to next 4kB boundary and stored as number of SCCBs - * (4kB Buffers) limitation: 256 x 4kB - */ - if (get_user(obuf, (unsigned int __user *) arg) == 0) { - if (obuf & 0xFFF) - sclp_ioctls.max_sccb = (obuf >> 12) + 1; - else - sclp_ioctls.max_sccb = (obuf >> 12); - } else - rc = -EFAULT; - break; - case TIOCSCLPGOBUF: - /* get the maximum buffers size for output */ - obuf = sclp_ioctls.max_sccb << 12; - if (put_user(obuf, (unsigned int __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPGKBUF: - /* get the number of buffers got from kernel at startup */ - if (put_user(sclp_ioctls.kmem_sccb, (unsigned short __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSCASE: - /* enable/disable conversion from upper to lower case */ - if (get_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPGCASE: - /* Is conversion from upper to lower case of input enabled? */ - if (put_user(sclp_ioctls.tolower, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSDELIM: - /* - * set special character used for separating upper and - * lower case, 0x00 disables this feature - */ - if (get_user(sclp_ioctls.delim, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPGDELIM: - /* - * get special character used for separating upper and - * lower case, 0x00 disables this feature - */ - if (put_user(sclp_ioctls.delim, (unsigned char __user *) arg)) - rc = -EFAULT; - break; - case TIOCSCLPSINIT: - /* set initial (default) sclp ioctls */ - sclp_ioctls = sclp_ioctls_init; - check = 1; - break; - default: - rc = -ENOIOCTLCMD; - break; - } - if (check) { - spin_lock_irqsave(&sclp_tty_lock, flags); - if (sclp_ttybuf != NULL) { - sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab); - sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns); - } - spin_unlock_irqrestore(&sclp_tty_lock, flags); - } - return rc; -} - /* * This routine returns the numbers of characters the tty driver * will accept for queuing to be written. This number is subject @@ -268,7 +125,6 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc) struct sclp_buffer, list); spin_unlock_irqrestore(&sclp_tty_lock, flags); } while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); - wake_up(&sclp_tty_waitq); /* check if the tty needs a wake up call */ if (sclp_tty != NULL) { tty_wakeup(sclp_tty); @@ -316,37 +172,37 @@ sclp_tty_timeout(unsigned long data) /* * Write a string to the sclp tty. */ -static void -sclp_tty_write_string(const unsigned char *str, int count) +static int sclp_tty_write_string(const unsigned char *str, int count, int may_fail) { unsigned long flags; void *page; int written; + int overall_written; struct sclp_buffer *buf; if (count <= 0) - return; + return 0; + overall_written = 0; spin_lock_irqsave(&sclp_tty_lock, flags); do { /* Create a sclp output buffer if none exists yet */ if (sclp_ttybuf == NULL) { while (list_empty(&sclp_tty_pages)) { spin_unlock_irqrestore(&sclp_tty_lock, flags); - if (in_interrupt()) - sclp_sync_wait(); + if (may_fail) + goto out; else - wait_event(sclp_tty_waitq, - !list_empty(&sclp_tty_pages)); + sclp_sync_wait(); spin_lock_irqsave(&sclp_tty_lock, flags); } page = sclp_tty_pages.next; list_del((struct list_head *) page); - sclp_ttybuf = sclp_make_buffer(page, - sclp_ioctls.columns, - sclp_ioctls.htab); + sclp_ttybuf = sclp_make_buffer(page, sclp_tty_columns, + SPACES_PER_TAB); } /* try to write the string to the current output buffer */ written = sclp_write(sclp_ttybuf, str, count); + overall_written += written; if (written == count) break; /* @@ -363,27 +219,17 @@ sclp_tty_write_string(const unsigned char *str, int count) count -= written; } while (count > 0); /* Setup timer to output current console buffer after 1/10 second */ - if (sclp_ioctls.final_nl) { - if (sclp_ttybuf != NULL && - sclp_chars_in_buffer(sclp_ttybuf) != 0 && - !timer_pending(&sclp_tty_timer)) { - init_timer(&sclp_tty_timer); - sclp_tty_timer.function = sclp_tty_timeout; - sclp_tty_timer.data = 0UL; - sclp_tty_timer.expires = jiffies + HZ/10; - add_timer(&sclp_tty_timer); - } - } else { - if (sclp_ttybuf != NULL && - sclp_chars_in_buffer(sclp_ttybuf) != 0) { - buf = sclp_ttybuf; - sclp_ttybuf = NULL; - spin_unlock_irqrestore(&sclp_tty_lock, flags); - __sclp_ttybuf_emit(buf); - spin_lock_irqsave(&sclp_tty_lock, flags); - } + if (sclp_ttybuf && sclp_chars_in_buffer(sclp_ttybuf) && + !timer_pending(&sclp_tty_timer)) { + init_timer(&sclp_tty_timer); + sclp_tty_timer.function = sclp_tty_timeout; + sclp_tty_timer.data = 0UL; + sclp_tty_timer.expires = jiffies + HZ/10; + add_timer(&sclp_tty_timer); } spin_unlock_irqrestore(&sclp_tty_lock, flags); +out: + return overall_written; } /* @@ -395,11 +241,10 @@ static int sclp_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { if (sclp_tty_chars_count > 0) { - sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); sclp_tty_chars_count = 0; } - sclp_tty_write_string(buf, count); - return count; + return sclp_tty_write_string(buf, count, 1); } /* @@ -417,9 +262,10 @@ sclp_tty_put_char(struct tty_struct *tty, unsigned char ch) { sclp_tty_chars[sclp_tty_chars_count++] = ch; if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) { - sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); sclp_tty_chars_count = 0; - } return 1; + } + return 1; } /* @@ -430,7 +276,7 @@ static void sclp_tty_flush_chars(struct tty_struct *tty) { if (sclp_tty_chars_count > 0) { - sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); sclp_tty_chars_count = 0; } } @@ -469,7 +315,7 @@ static void sclp_tty_flush_buffer(struct tty_struct *tty) { if (sclp_tty_chars_count > 0) { - sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count); + sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0); sclp_tty_chars_count = 0; } } @@ -517,9 +363,7 @@ sclp_tty_input(unsigned char* buf, unsigned int count) * modifiy original string, * returns length of resulting string */ -static int -sclp_switch_cases(unsigned char *buf, int count, - unsigned char delim, int tolower) +static int sclp_switch_cases(unsigned char *buf, int count) { unsigned char *ip, *op; int toggle; @@ -529,9 +373,9 @@ sclp_switch_cases(unsigned char *buf, int count, ip = op = buf; while (count-- > 0) { /* compare with special character */ - if (*ip == delim) { + if (*ip == CASE_DELIMITER) { /* followed by another special character? */ - if (count && ip[1] == delim) { + if (count && ip[1] == CASE_DELIMITER) { /* * ... then put a single copy of the special * character to the output string @@ -550,7 +394,7 @@ sclp_switch_cases(unsigned char *buf, int count, /* not the special character */ if (toggle) /* but case switching is on */ - if (tolower) + if (sclp_tty_tolower) /* switch to uppercase */ *op++ = _ebc_toupper[(int) *ip++]; else @@ -570,30 +414,12 @@ sclp_get_input(unsigned char *start, unsigned char *end) int count; count = end - start; - /* - * if set in ioctl convert EBCDIC to lower case - * (modify original input in SCCB) - */ - if (sclp_ioctls.tolower) + if (sclp_tty_tolower) EBC_TOLOWER(start, count); - - /* - * if set in ioctl find out characters in lower or upper case - * (depends on current case) separated by a special character, - * works on EBCDIC - */ - if (sclp_ioctls.delim) - count = sclp_switch_cases(start, count, - sclp_ioctls.delim, - sclp_ioctls.tolower); - + count = sclp_switch_cases(start, count); /* convert EBCDIC to ASCII (modify original input in SCCB) */ sclp_ebcasc_str(start, count); - /* if set in ioctl write operators input to console */ - if (sclp_ioctls.echo) - sclp_tty_write(sclp_tty, start, count); - /* transfer input to high level driver */ sclp_tty_input(start, count); } @@ -717,7 +543,6 @@ static const struct tty_operations sclp_ops = { .write_room = sclp_tty_write_room, .chars_in_buffer = sclp_tty_chars_in_buffer, .flush_buffer = sclp_tty_flush_buffer, - .ioctl = sclp_tty_ioctl, }; static int __init @@ -736,9 +561,6 @@ sclp_tty_init(void) rc = sclp_rw_init(); if (rc) { - printk(KERN_ERR SCLP_TTY_PRINT_HEADER - "could not register tty - " - "sclp_rw_init returned %d\n", rc); put_tty_driver(driver); return rc; } @@ -754,7 +576,6 @@ sclp_tty_init(void) } INIT_LIST_HEAD(&sclp_tty_outqueue); spin_lock_init(&sclp_tty_lock); - init_waitqueue_head(&sclp_tty_waitq); init_timer(&sclp_tty_timer); sclp_ttybuf = NULL; sclp_tty_buffer_count = 0; @@ -763,11 +584,10 @@ sclp_tty_init(void) * save 4 characters for the CPU number * written at start of each line by VM/CP */ - sclp_ioctls_init.columns = 76; + sclp_tty_columns = 76; /* case input lines to lowercase */ - sclp_ioctls_init.tolower = 1; + sclp_tty_tolower = 1; } - sclp_ioctls = sclp_ioctls_init; sclp_tty_chars_count = 0; sclp_tty = NULL; @@ -792,9 +612,6 @@ sclp_tty_init(void) tty_set_operations(driver, &sclp_ops); rc = tty_register_driver(driver); if (rc) { - printk(KERN_ERR SCLP_TTY_PRINT_HEADER - "could not register tty - " - "tty_register_driver returned %d\n", rc); put_tty_driver(driver); return rc; } diff --git a/drivers/s390/char/sclp_tty.h b/drivers/s390/char/sclp_tty.h index 0ce2c1fc534..4b965b22fec 100644 --- a/drivers/s390/char/sclp_tty.h +++ b/drivers/s390/char/sclp_tty.h @@ -11,61 +11,8 @@ #ifndef __SCLP_TTY_H__ #define __SCLP_TTY_H__ -#include <linux/ioctl.h> -#include <linux/termios.h> #include <linux/tty_driver.h> -/* This is the type of data structures storing sclp ioctl setting. */ -struct sclp_ioctls { - unsigned short htab; - unsigned char echo; - unsigned short columns; - unsigned char final_nl; - unsigned short max_sccb; - unsigned short kmem_sccb; /* can't be modified at run time */ - unsigned char tolower; - unsigned char delim; -}; - -/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */ -#define SCLP_IOCTL_LETTER 'B' - -/* set width of horizontal tabulator */ -#define TIOCSCLPSHTAB _IOW(SCLP_IOCTL_LETTER, 0, unsigned short) -/* enable/disable echo of input (independent from line discipline) */ -#define TIOCSCLPSECHO _IOW(SCLP_IOCTL_LETTER, 1, unsigned char) -/* set number of colums for output */ -#define TIOCSCLPSCOLS _IOW(SCLP_IOCTL_LETTER, 2, unsigned short) -/* enable/disable writing without final new line character */ -#define TIOCSCLPSNL _IOW(SCLP_IOCTL_LETTER, 4, signed char) -/* set the maximum buffers size for output, rounded up to next 4kB boundary */ -#define TIOCSCLPSOBUF _IOW(SCLP_IOCTL_LETTER, 5, unsigned short) -/* set initial (default) sclp ioctls */ -#define TIOCSCLPSINIT _IO(SCLP_IOCTL_LETTER, 6) -/* enable/disable conversion from upper to lower case of input */ -#define TIOCSCLPSCASE _IOW(SCLP_IOCTL_LETTER, 7, unsigned char) -/* set special character used for separating upper and lower case, */ -/* 0x00 disables this feature */ -#define TIOCSCLPSDELIM _IOW(SCLP_IOCTL_LETTER, 9, unsigned char) - -/* get width of horizontal tabulator */ -#define TIOCSCLPGHTAB _IOR(SCLP_IOCTL_LETTER, 10, unsigned short) -/* Is echo of input enabled ? (independent from line discipline) */ -#define TIOCSCLPGECHO _IOR(SCLP_IOCTL_LETTER, 11, unsigned char) -/* get number of colums for output */ -#define TIOCSCLPGCOLS _IOR(SCLP_IOCTL_LETTER, 12, unsigned short) -/* Is writing without final new line character enabled ? */ -#define TIOCSCLPGNL _IOR(SCLP_IOCTL_LETTER, 14, signed char) -/* get the maximum buffers size for output */ -#define TIOCSCLPGOBUF _IOR(SCLP_IOCTL_LETTER, 15, unsigned short) -/* Is conversion from upper to lower case of input enabled ? */ -#define TIOCSCLPGCASE _IOR(SCLP_IOCTL_LETTER, 17, unsigned char) -/* get special character used for separating upper and lower case, */ -/* 0x00 disables this feature */ -#define TIOCSCLPGDELIM _IOR(SCLP_IOCTL_LETTER, 19, unsigned char) -/* get the number of buffers/pages got from kernel at startup */ -#define TIOCSCLPGKBUF _IOR(SCLP_IOCTL_LETTER, 20, unsigned short) - extern struct tty_driver *sclp_tty_driver; #endif /* __SCLP_TTY_H__ */ diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 3e577f655b1..ad51738c426 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -27,7 +27,6 @@ #include <asm/uaccess.h> #include "sclp.h" -#define SCLP_VT220_PRINT_HEADER "sclp vt220 tty driver: " #define SCLP_VT220_MAJOR TTY_MAJOR #define SCLP_VT220_MINOR 65 #define SCLP_VT220_DRIVER_NAME "sclp_vt220" @@ -82,8 +81,8 @@ static struct sclp_vt220_request *sclp_vt220_current_request; /* Number of characters in current request buffer */ static int sclp_vt220_buffered_chars; -/* Flag indicating whether this driver has already been initialized */ -static int sclp_vt220_initialized = 0; +/* Counter controlling core driver initialization. */ +static int __initdata sclp_vt220_init_count; /* Flag indicating that sclp_vt220_current_request should really * have been already queued but wasn't because the SCLP was processing @@ -609,10 +608,8 @@ sclp_vt220_flush_buffer(struct tty_struct *tty) sclp_vt220_emit_current(); } -/* - * Initialize all relevant components and register driver with system. - */ -static void __init __sclp_vt220_cleanup(void) +/* Release allocated pages. */ +static void __init __sclp_vt220_free_pages(void) { struct list_head *page, *p; @@ -623,21 +620,30 @@ static void __init __sclp_vt220_cleanup(void) else free_bootmem((unsigned long) page, PAGE_SIZE); } - if (!list_empty(&sclp_vt220_register.list)) - sclp_unregister(&sclp_vt220_register); - sclp_vt220_initialized = 0; } -static int __init __sclp_vt220_init(void) +/* Release memory and unregister from sclp core. Controlled by init counting - + * only the last invoker will actually perform these actions. */ +static void __init __sclp_vt220_cleanup(void) +{ + sclp_vt220_init_count--; + if (sclp_vt220_init_count != 0) + return; + sclp_unregister(&sclp_vt220_register); + __sclp_vt220_free_pages(); +} + +/* Allocate buffer pages and register with sclp core. Controlled by init + * counting - only the first invoker will actually perform these actions. */ +static int __init __sclp_vt220_init(int num_pages) { void *page; int i; - int num_pages; int rc; - if (sclp_vt220_initialized) + sclp_vt220_init_count++; + if (sclp_vt220_init_count != 1) return 0; - sclp_vt220_initialized = 1; spin_lock_init(&sclp_vt220_lock); INIT_LIST_HEAD(&sclp_vt220_empty); INIT_LIST_HEAD(&sclp_vt220_outqueue); @@ -649,24 +655,22 @@ static int __init __sclp_vt220_init(void) sclp_vt220_flush_later = 0; /* Allocate pages for output buffering */ - num_pages = slab_is_available() ? MAX_KMEM_PAGES : MAX_CONSOLE_PAGES; for (i = 0; i < num_pages; i++) { if (slab_is_available()) page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); else page = alloc_bootmem_low_pages(PAGE_SIZE); if (!page) { - __sclp_vt220_cleanup(); - return -ENOMEM; + rc = -ENOMEM; + goto out; } list_add_tail((struct list_head *) page, &sclp_vt220_empty); } rc = sclp_register(&sclp_vt220_register); +out: if (rc) { - printk(KERN_ERR SCLP_VT220_PRINT_HEADER - "could not register vt220 - " - "sclp_register returned %d\n", rc); - __sclp_vt220_cleanup(); + __sclp_vt220_free_pages(); + sclp_vt220_init_count--; } return rc; } @@ -689,15 +693,13 @@ static int __init sclp_vt220_tty_init(void) { struct tty_driver *driver; int rc; - int cleanup; /* Note: we're not testing for CONSOLE_IS_SCLP here to preserve * symmetry between VM and LPAR systems regarding ttyS1. */ driver = alloc_tty_driver(1); if (!driver) return -ENOMEM; - cleanup = !sclp_vt220_initialized; - rc = __sclp_vt220_init(); + rc = __sclp_vt220_init(MAX_KMEM_PAGES); if (rc) goto out_driver; @@ -713,18 +715,13 @@ static int __init sclp_vt220_tty_init(void) tty_set_operations(driver, &sclp_vt220_ops); rc = tty_register_driver(driver); - if (rc) { - printk(KERN_ERR SCLP_VT220_PRINT_HEADER - "could not register tty - " - "tty_register_driver returned %d\n", rc); + if (rc) goto out_init; - } sclp_vt220_driver = driver; return 0; out_init: - if (cleanup) - __sclp_vt220_cleanup(); + __sclp_vt220_cleanup(); out_driver: put_tty_driver(driver); return rc; @@ -773,10 +770,9 @@ sclp_vt220_con_init(void) { int rc; - INIT_LIST_HEAD(&sclp_vt220_register.list); if (!CONSOLE_IS_SCLP) return 0; - rc = __sclp_vt220_init(); + rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); if (rc) return rc; /* Attach linux console */ diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index 874adf365e4..22ca34361ed 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -196,7 +196,7 @@ tape_34xx_erp_retry(struct tape_request *request) static int tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb) { - if (irb->scsw.dstat == 0x85 /* READY */) { + if (irb->scsw.cmd.dstat == 0x85) { /* READY */ /* A medium was inserted in the drive. */ DBF_EVENT(6, "xuud med\n"); tape_34xx_delete_sbid_from(device, 0); @@ -844,22 +844,22 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request, if (request == NULL) return tape_34xx_unsolicited_irq(device, irb); - if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) && - (irb->scsw.dstat & DEV_STAT_DEV_END) && + if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) && + (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) { /* Write at end of volume */ PRINT_INFO("End of volume\n"); /* XXX */ return tape_34xx_erp_failed(request, -ENOSPC); } - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) return tape_34xx_unit_check(device, request, irb); - if (irb->scsw.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { /* * A unit exception occurs on skipping over a tapemark block. */ - if (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { if (request->op == TO_BSB || request->op == TO_FSB) request->rescnt++; else diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index 42ce7915fc5..839987618ff 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -837,13 +837,13 @@ tape_3590_erp_retry(struct tape_device *device, struct tape_request *request, static int tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb) { - if (irb->scsw.dstat == DEV_STAT_CHN_END) + if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END) /* Probably result of halt ssch */ return TAPE_IO_PENDING; - else if (irb->scsw.dstat == 0x85) + else if (irb->scsw.cmd.dstat == 0x85) /* Device Ready */ DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id); - else if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { tape_3590_schedule_work(device, TO_READ_ATTMSG); } else { DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id); @@ -1515,18 +1515,19 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request, if (request == NULL) return tape_3590_unsolicited_irq(device, irb); - if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) && - (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) { + if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) && + (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) && + (request->op == TO_WRI)) { /* Write at end of volume */ DBF_EVENT(2, "End of volume\n"); return tape_3590_erp_failed(device, request, irb, -ENOSPC); } - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) return tape_3590_unit_check(device, request, irb); - if (irb->scsw.dstat & DEV_STAT_DEV_END) { - if (irb->scsw.dstat == DEV_STAT_UNIT_EXCEP) { + if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { + if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) { if (request->op == TO_FSB || request->op == TO_BSB) request->rescnt++; else @@ -1536,12 +1537,12 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request, return tape_3590_done(device, request); } - if (irb->scsw.dstat & DEV_STAT_CHN_END) { + if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) { DBF_EVENT(2, "cannel end\n"); return TAPE_IO_PENDING; } - if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { DBF_EVENT(2, "Unit Attention when busy..\n"); return TAPE_IO_PENDING; } diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index c20e3c54834..181a5441af1 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -839,7 +839,7 @@ tape_dump_sense(struct tape_device* device, struct tape_request *request, PRINT_INFO("-------------------------------------------------\n"); PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n", - irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa); + irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa); PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id); if (request != NULL) PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]); @@ -867,7 +867,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request, else op = "---"; DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n", - irb->scsw.dstat,irb->scsw.cstat); + irb->scsw.cmd.dstat, irb->scsw.cmd.cstat); DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op); sptr = (unsigned int *) irb->ecw; DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]); @@ -1083,10 +1083,11 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) * error might still apply. So we just schedule the request to be * started later. */ - if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC) && + if (irb->scsw.cmd.cc != 0 && + (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) && (request->status == TAPE_REQUEST_IN_IO)) { DBF_EVENT(3,"(%08x): deferred cc=%i, fctl=%i. restarting\n", - device->cdev_id, irb->scsw.cc, irb->scsw.fctl); + device->cdev_id, irb->scsw.cmd.cc, irb->scsw.cmd.fctl); request->status = TAPE_REQUEST_QUEUED; schedule_delayed_work(&device->tape_dnr, HZ); return; @@ -1094,8 +1095,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) /* May be an unsolicited irq */ if(request != NULL) - request->rescnt = irb->scsw.count; - else if ((irb->scsw.dstat == 0x85 || irb->scsw.dstat == 0x80) && + request->rescnt = irb->scsw.cmd.count; + else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) && !list_empty(&device->req_queue)) { /* Not Ready to Ready after long busy ? */ struct tape_request *req; @@ -1111,7 +1112,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) return; } } - if (irb->scsw.dstat != 0x0c) { + if (irb->scsw.cmd.dstat != 0x0c) { /* Set the 'ONLINE' flag depending on sense byte 1 */ if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE) device->tape_generic_status |= GMT_ONLINE(~0); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 5043150019a..a7fe6302c98 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -663,7 +663,7 @@ static int tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) { /* Handle ATTN. Schedule tasklet to read aid. */ - if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { if (!tp->throttle) tty3270_issue_read(tp, 0); else @@ -671,11 +671,11 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) } if (rq) { - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) rq->rc = -EIO; else /* Normal end. Copy residual count. */ - rq->rescnt = irb->scsw.count; + rq->rescnt = irb->scsw.cmd.count; } return RAW3270_IO_DONE; } @@ -1792,15 +1792,12 @@ static int __init tty3270_init(void) tty_set_operations(driver, &tty3270_ops); ret = tty_register_driver(driver); if (ret) { - printk(KERN_ERR "tty3270 registration failed with %d\n", ret); put_tty_driver(driver); return ret; } tty3270_driver = driver; ret = raw3270_register_notifier(tty3270_notifier); if (ret) { - printk(KERN_ERR "tty3270 notifier registration failed " - "with %d\n", ret); put_tty_driver(driver); return ret; diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 2f419b0ea62..401ea84b305 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -61,30 +61,24 @@ static int vmcp_release(struct inode *inode, struct file *file) static ssize_t vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { - size_t tocopy; + ssize_t ret; + size_t size; struct vmcp_session *session; - session = (struct vmcp_session *)file->private_data; + session = file->private_data; if (mutex_lock_interruptible(&session->mutex)) return -ERESTARTSYS; if (!session->response) { mutex_unlock(&session->mutex); return 0; } - if (*ppos > session->resp_size) { - mutex_unlock(&session->mutex); - return 0; - } - tocopy = min(session->resp_size - (size_t) (*ppos), count); - tocopy = min(tocopy, session->bufsize - (size_t) (*ppos)); + size = min_t(size_t, session->resp_size, session->bufsize); + ret = simple_read_from_buffer(buff, count, ppos, + session->response, size); - if (copy_to_user(buff, session->response + (*ppos), tocopy)) { - mutex_unlock(&session->mutex); - return -EFAULT; - } mutex_unlock(&session->mutex); - *ppos += tocopy; - return tocopy; + + return ret; } static ssize_t @@ -198,27 +192,23 @@ static int __init vmcp_init(void) PRINT_WARN("z/VM CP interface is only available under z/VM\n"); return -ENODEV; } + vmcp_debug = debug_register("vmcp", 1, 1, 240); - if (!vmcp_debug) { - PRINT_ERR("z/VM CP interface not loaded. Could not register " - "debug feature\n"); + if (!vmcp_debug) return -ENOMEM; - } + ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view); if (ret) { - PRINT_ERR("z/VM CP interface not loaded. Could not register " - "debug feature view. Error code: %d\n", ret); debug_unregister(vmcp_debug); return ret; } + ret = misc_register(&vmcp_dev); if (ret) { - PRINT_ERR("z/VM CP interface not loaded. Could not register " - "misc device. Error code: %d\n", ret); debug_unregister(vmcp_debug); return ret; } - PRINT_INFO("z/VM CP interface loaded\n"); + return 0; } @@ -226,7 +216,6 @@ static void __exit vmcp_exit(void) { misc_deregister(&vmcp_dev); debug_unregister(vmcp_debug); - PRINT_INFO("z/VM CP interface unloaded.\n"); } module_init(vmcp_init); diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 2c2428cc05d..a246bc73ae6 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -216,9 +216,7 @@ static int vmlogrdr_get_recording_class_AB(void) char *tail; int len,i; - printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command); cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); - printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response); len = strnlen(cp_response,sizeof(cp_response)); // now the parsing tail=strnchr(cp_response,len,'='); @@ -268,11 +266,7 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, logptr->recording_name, qid_string); - printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", - cp_command); cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); - printk (KERN_DEBUG "vmlogrdr: recording response: %s", - cp_response); } memset(cp_command, 0x00, sizeof(cp_command)); @@ -282,10 +276,7 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, onoff, qid_string); - printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); - printk (KERN_DEBUG "vmlogrdr: recording response: %s", - cp_response); /* The recording command will usually answer with 'Command complete' * on success, but when the specific service was never connected * before then there might be an additional informational message @@ -567,10 +558,7 @@ static ssize_t vmlogrdr_purge_store(struct device * dev, "RECORDING %s PURGE ", priv->recording_name); - printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); - printk (KERN_DEBUG "vmlogrdr: recording response: %s", - cp_response); return count; } @@ -682,28 +670,20 @@ static int vmlogrdr_register_driver(void) /* Register with iucv driver */ ret = iucv_register(&vmlogrdr_iucv_handler, 1); - if (ret) { - printk (KERN_ERR "vmlogrdr: failed to register with " - "iucv driver\n"); + if (ret) goto out; - } ret = driver_register(&vmlogrdr_driver); - if (ret) { - printk(KERN_ERR "vmlogrdr: failed to register driver.\n"); + if (ret) goto out_iucv; - } ret = driver_create_file(&vmlogrdr_driver, &driver_attr_recording_status); - if (ret) { - printk(KERN_ERR "vmlogrdr: failed to add driver attribute.\n"); + if (ret) goto out_driver; - } vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr"); if (IS_ERR(vmlogrdr_class)) { - printk(KERN_ERR "vmlogrdr: failed to create class.\n"); ret = PTR_ERR(vmlogrdr_class); vmlogrdr_class = NULL; goto out_attr; @@ -871,12 +851,10 @@ static int __init vmlogrdr_init(void) rc = vmlogrdr_register_cdev(dev); if (rc) goto cleanup; - printk (KERN_INFO "vmlogrdr: driver loaded\n"); return 0; cleanup: vmlogrdr_cleanup(); - printk (KERN_ERR "vmlogrdr: driver not loaded.\n"); return rc; } @@ -884,7 +862,6 @@ cleanup: static void __exit vmlogrdr_exit(void) { vmlogrdr_cleanup(); - printk (KERN_INFO "vmlogrdr: driver unloaded\n"); return; } diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 83ae9a852f0..49cba9effe8 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -277,7 +277,8 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, struct urdev *urd; TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n", - intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count); + intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, + irb->scsw.cmd.count); if (!intparm) { TRACE("ur_int_handler: unsolicited interrupt\n"); @@ -288,7 +289,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, /* On special conditions irb is an error pointer */ if (IS_ERR(irb)) urd->io_request_rc = PTR_ERR(irb); - else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END)) urd->io_request_rc = 0; else urd->io_request_rc = -EIO; diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 19f8389291b..56b3eab019c 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -92,23 +92,15 @@ static int vmwdt_keepalive(void) func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; ret = __diag288(func, vmwdt_interval, ebc_cmd, len); + WARN_ON(ret != 0); kfree(ebc_cmd); - - if (ret) { - printk(KERN_WARNING "%s: problem setting interval %d, " - "cmd %s\n", __func__, vmwdt_interval, - vmwdt_cmd); - } return ret; } static int vmwdt_disable(void) { int ret = __diag288(wdt_cancel, 0, "", 0); - if (ret) { - printk(KERN_WARNING "%s: problem disabling watchdog\n", - __func__); - } + WARN_ON(ret != 0); return ret; } @@ -121,10 +113,8 @@ static int __init vmwdt_probe(void) static char __initdata ebc_begin[] = { 194, 197, 199, 201, 213 }; - if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) { - printk(KERN_INFO "z/VM watchdog not available\n"); + if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) return -EINVAL; - } return vmwdt_disable(); } diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index bbbd14e9d48..047dd92ae80 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -223,12 +223,10 @@ static int __init init_cpu_info(enum arch_id arch) /* get info for boot cpu from lowcore, stored in the HSA */ sa = kmalloc(sizeof(*sa), GFP_KERNEL); - if (!sa) { - ERROR_MSG("kmalloc failed: %s: %i\n",__func__, __LINE__); + if (!sa) return -ENOMEM; - } if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { - ERROR_MSG("could not copy from HSA\n"); + TRACE("could not copy from HSA\n"); kfree(sa); return -EIO; } @@ -511,6 +509,8 @@ static void __init set_s390x_lc_mask(union save_area *map) */ static int __init sys_info_init(enum arch_id arch) { + int rc; + switch (arch) { case ARCH_S390X: MSG("DETECTED 'S390X (64 bit) OS'\n"); @@ -529,10 +529,9 @@ static int __init sys_info_init(enum arch_id arch) return -EINVAL; } sys_info.arch = arch; - if (init_cpu_info(arch)) { - ERROR_MSG("get cpu info failed\n"); - return -ENOMEM; - } + rc = init_cpu_info(arch); + if (rc) + return rc; sys_info.mem_size = real_memory_size; return 0; @@ -544,12 +543,12 @@ static int __init check_sdias(void) rc = sclp_sdias_blk_count(); if (rc < 0) { - ERROR_MSG("Could not determine HSA size\n"); + TRACE("Could not determine HSA size\n"); return rc; } act_hsa_size = (rc - 1) * PAGE_SIZE; if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { - ERROR_MSG("HSA size too small: %i\n", act_hsa_size); + TRACE("HSA size too small: %i\n", act_hsa_size); return -EINVAL; } return 0; @@ -590,16 +589,12 @@ static int __init zcore_init(void) goto fail; rc = check_sdias(); - if (rc) { - ERROR_MSG("Dump initialization failed\n"); + if (rc) goto fail; - } rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); - if (rc) { - ERROR_MSG("sdial memcpy for arch id failed\n"); + if (rc) goto fail; - } #ifndef __s390x__ if (arch == ARCH_S390X) { @@ -610,10 +605,8 @@ static int __init zcore_init(void) #endif rc = sys_info_init(arch); - if (rc) { - ERROR_MSG("arch init failed\n"); + if (rc) goto fail; - } zcore_header_init(arch, &zcore_header); diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index cfaf77b320f..91e9e3f3073 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,9 +2,11 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o scsw.o \ + fcx.o itcw.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o +obj-$(CONFIG_CHSC_SCH) += chsc_sch.o obj-$(CONFIG_CCWGROUP) += ccwgroup.o obj-$(CONFIG_QDIO) += qdio.o diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index b7a07a86629..fe6cea15bba 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -15,6 +15,7 @@ #include <linux/rcupdate.h> #include <asm/airq.h> +#include <asm/isc.h> #include "cio.h" #include "cio_debug.h" @@ -33,15 +34,15 @@ struct airq_t { void *drv_data; }; -static union indicator_t indicators; -static struct airq_t *airqs[NR_AIRQS]; +static union indicator_t indicators[MAX_ISC]; +static struct airq_t *airqs[MAX_ISC][NR_AIRQS]; -static int register_airq(struct airq_t *airq) +static int register_airq(struct airq_t *airq, u8 isc) { int i; for (i = 0; i < NR_AIRQS; i++) - if (!cmpxchg(&airqs[i], NULL, airq)) + if (!cmpxchg(&airqs[isc][i], NULL, airq)) return i; return -ENOMEM; } @@ -50,18 +51,21 @@ static int register_airq(struct airq_t *airq) * s390_register_adapter_interrupt() - register adapter interrupt handler * @handler: adapter handler to be registered * @drv_data: driver data passed with each call to the handler + * @isc: isc for which the handler should be called * * Returns: * Pointer to the indicator to be used on success * ERR_PTR() if registration failed */ void *s390_register_adapter_interrupt(adapter_int_handler_t handler, - void *drv_data) + void *drv_data, u8 isc) { struct airq_t *airq; char dbf_txt[16]; int ret; + if (isc > MAX_ISC) + return ERR_PTR(-EINVAL); airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); if (!airq) { ret = -ENOMEM; @@ -69,34 +73,35 @@ void *s390_register_adapter_interrupt(adapter_int_handler_t handler, } airq->handler = handler; airq->drv_data = drv_data; - ret = register_airq(airq); - if (ret < 0) - kfree(airq); + + ret = register_airq(airq, isc); out: snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); CIO_TRACE_EVENT(4, dbf_txt); - if (ret < 0) + if (ret < 0) { + kfree(airq); return ERR_PTR(ret); - else - return &indicators.byte[ret]; + } else + return &indicators[isc].byte[ret]; } EXPORT_SYMBOL(s390_register_adapter_interrupt); /** * s390_unregister_adapter_interrupt - unregister adapter interrupt handler * @ind: indicator for which the handler is to be unregistered + * @isc: interruption subclass */ -void s390_unregister_adapter_interrupt(void *ind) +void s390_unregister_adapter_interrupt(void *ind, u8 isc) { struct airq_t *airq; char dbf_txt[16]; int i; - i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]); + i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); CIO_TRACE_EVENT(4, dbf_txt); - indicators.byte[i] = 0; - airq = xchg(&airqs[i], NULL); + indicators[isc].byte[i] = 0; + airq = xchg(&airqs[isc][i], NULL); /* * Allow interrupts to complete. This will ensure that the airq handle * is no longer referenced by any interrupt handler. @@ -108,7 +113,7 @@ EXPORT_SYMBOL(s390_unregister_adapter_interrupt); #define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) -void do_adapter_IO(void) +void do_adapter_IO(u8 isc) { int w; int i; @@ -120,22 +125,22 @@ void do_adapter_IO(void) * fetch operations. */ for (w = 0; w < NR_AIRQ_WORDS; w++) { - word = indicators.word[w]; + word = indicators[isc].word[w]; i = w * NR_AIRQS_PER_WORD; /* * Check bytes within word for active indicators. */ while (word) { if (word & INDICATOR_MASK) { - airq = airqs[i]; + airq = airqs[isc][i]; if (likely(airq)) - airq->handler(&indicators.byte[i], + airq->handler(&indicators[isc].byte[i], airq->drv_data); else /* * Reset ill-behaved indicator. */ - indicators.byte[i] = 0; + indicators[isc].byte[i] = 0; } word <<= 8; i++; diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 297cdceb0ca..db00b059173 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -18,6 +18,7 @@ #include <asm/chpid.h> #include <asm/sclp.h> +#include "../s390mach.h" #include "cio.h" #include "css.h" #include "ioasm.h" @@ -94,6 +95,7 @@ u8 chp_get_sch_opm(struct subchannel *sch) } return opm; } +EXPORT_SYMBOL_GPL(chp_get_sch_opm); /** * chp_is_registered - check if a channel-path is registered @@ -121,11 +123,8 @@ static int s390_vary_chpid(struct chp_id chpid, int on) CIO_TRACE_EVENT(2, dbf_text); status = chp_get_status(chpid); - if (!on && !status) { - printk(KERN_ERR "cio: chpid %x.%02x is already offline\n", - chpid.cssid, chpid.id); - return -EINVAL; - } + if (!on && !status) + return 0; set_chp_logically_online(chpid, on); chsc_chp_vary(chpid, on); @@ -141,21 +140,14 @@ static ssize_t chp_measurement_chars_read(struct kobject *kobj, { struct channel_path *chp; struct device *device; - unsigned int size; device = container_of(kobj, struct device, kobj); chp = to_channelpath(device); if (!chp->cmg_chars) return 0; - size = sizeof(struct cmg_chars); - - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, chp->cmg_chars + off, count); - return count; + return memory_read_from_buffer(buf, count, &off, + chp->cmg_chars, sizeof(struct cmg_chars)); } static struct bin_attribute chp_measurement_chars_attr = { @@ -405,7 +397,7 @@ int chp_new(struct chp_id chpid) chpid.id); /* Obtain channel path description and fill it in. */ - ret = chsc_determine_channel_path_description(chpid, &chp->desc); + ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); if (ret) goto out_free; if ((chp->desc.flags & 0x80) == 0) { @@ -413,8 +405,7 @@ int chp_new(struct chp_id chpid) goto out_free; } /* Get channel-measurement characteristics. */ - if (css_characteristics_avail && css_chsc_characteristics.scmc - && css_chsc_characteristics.secm) { + if (css_chsc_characteristics.scmc && css_chsc_characteristics.secm) { ret = chsc_get_channel_measurement_chars(chp); if (ret) goto out_free; @@ -476,26 +467,74 @@ void *chp_get_chp_desc(struct chp_id chpid) /** * chp_process_crw - process channel-path status change - * @id: channel-path ID number - * @status: non-zero if channel-path has become available, zero otherwise + * @crw0: channel report-word to handler + * @crw1: second channel-report word (always NULL) + * @overflow: crw overflow indication * * Handle channel-report-words indicating that the status of a channel-path * has changed. */ -void chp_process_crw(int id, int status) +static void chp_process_crw(struct crw *crw0, struct crw *crw1, + int overflow) { struct chp_id chpid; + if (overflow) { + css_schedule_eval_all(); + return; + } + CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, " + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", + crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, + crw0->erc, crw0->rsid); + /* + * Check for solicited machine checks. These are + * created by reset channel path and need not be + * handled here. + */ + if (crw0->slct) { + CIO_CRW_EVENT(2, "solicited machine check for " + "channel path %02X\n", crw0->rsid); + return; + } chp_id_init(&chpid); - chpid.id = id; - if (status) { + chpid.id = crw0->rsid; + switch (crw0->erc) { + case CRW_ERC_IPARM: /* Path has come. */ if (!chp_is_registered(chpid)) chp_new(chpid); chsc_chp_online(chpid); - } else + break; + case CRW_ERC_PERRI: /* Path has gone. */ + case CRW_ERC_PERRN: chsc_chp_offline(chpid); + break; + default: + CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n", + crw0->erc); + } } +int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (!(ssd->path_mask & mask)) + continue; + if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid)) + continue; + if ((ssd->fla_valid_mask & mask) && + ((ssd->fla[i] & link->fla_mask) != link->fla)) + continue; + return mask; + } + return 0; +} +EXPORT_SYMBOL_GPL(chp_ssd_get_mask); + static inline int info_bit_num(struct chp_id id) { return id.id + id.cssid * (__MAX_CHPID + 1); @@ -575,6 +614,7 @@ static void cfg_func(struct work_struct *work) { struct chp_id chpid; enum cfg_task_t t; + int rc; mutex_lock(&cfg_lock); t = cfg_none; @@ -589,14 +629,24 @@ static void cfg_func(struct work_struct *work) switch (t) { case cfg_configure: - sclp_chp_configure(chpid); - info_expire(); - chsc_chp_online(chpid); + rc = sclp_chp_configure(chpid); + if (rc) + CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)=" + "%d\n", chpid.cssid, chpid.id, rc); + else { + info_expire(); + chsc_chp_online(chpid); + } break; case cfg_deconfigure: - sclp_chp_deconfigure(chpid); - info_expire(); - chsc_chp_offline(chpid); + rc = sclp_chp_deconfigure(chpid); + if (rc) + CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)=" + "%d\n", chpid.cssid, chpid.id, rc); + else { + info_expire(); + chsc_chp_offline(chpid); + } break; case cfg_none: /* Get updated information after last change. */ @@ -654,10 +704,16 @@ static int cfg_wait_idle(void) static int __init chp_init(void) { struct chp_id chpid; + int ret; + ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw); + if (ret) + return ret; chp_wq = create_singlethread_workqueue("cio_chp"); - if (!chp_wq) + if (!chp_wq) { + s390_unregister_crw_handler(CRW_RSC_CPATH); return -ENOMEM; + } INIT_WORK(&cfg_work, cfg_func); init_waitqueue_head(&cfg_wait_queue); if (info_update()) diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 65286563c59..26c3d224617 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -12,12 +12,24 @@ #include <linux/device.h> #include <asm/chpid.h> #include "chsc.h" +#include "css.h" #define CHP_STATUS_STANDBY 0 #define CHP_STATUS_CONFIGURED 1 #define CHP_STATUS_RESERVED 2 #define CHP_STATUS_NOT_RECOGNIZED 3 +#define CHP_ONLINE 0 +#define CHP_OFFLINE 1 +#define CHP_VARY_ON 2 +#define CHP_VARY_OFF 3 + +struct chp_link { + struct chp_id chpid; + u32 fla_mask; + u16 fla; +}; + static inline int chp_test_bit(u8 *bitmap, int num) { int byte = num >> 3; @@ -42,12 +54,11 @@ int chp_get_status(struct chp_id chpid); u8 chp_get_sch_opm(struct subchannel *sch); int chp_is_registered(struct chp_id chpid); void *chp_get_chp_desc(struct chp_id chpid); -void chp_process_crw(int id, int available); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); int chp_new(struct chp_id chpid); void chp_cfg_schedule(struct chp_id chpid, int configure); void chp_cfg_cancel_deconfigure(struct chp_id chpid); int chp_info_get_status(struct chp_id chpid); - +int chp_ssd_get_mask(struct chsc_ssd_info *, struct chp_link *); #endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 5de86908b0d..65264a38057 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -2,8 +2,7 @@ * drivers/s390/cio/chsc.c * S/390 common I/O routines -- channel subsystem call * - * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation + * Copyright IBM Corp. 1999,2008 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) @@ -16,7 +15,9 @@ #include <asm/cio.h> #include <asm/chpid.h> +#include <asm/chsc.h> +#include "../s390mach.h" #include "css.h" #include "cio.h" #include "cio_debug.h" @@ -127,77 +128,12 @@ out_free: return ret; } -static int check_for_io_on_path(struct subchannel *sch, int mask) -{ - int cc; - - cc = stsch(sch->schid, &sch->schib); - if (cc) - return 0; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (device_trigger_verify(sch) != 0) - css_schedule_eval(sch->schid); - return; - } - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(sch); -} - static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { - int j; - int mask; - struct chp_id *chpid = data; - struct schib schib; - - for (j = 0; j < 8; j++) { - mask = 0x80 >> j; - if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[j] == chpid->id)) - break; - } - if (j >= 8) - return 0; - spin_lock_irq(sch->lock); - - stsch(sch->schid, &schib); - if (!css_sch_is_valid(&schib)) - goto out_unreg; - memcpy(&sch->schib, &schib, sizeof(struct schib)); - /* Check for single path devices. */ - if (sch->schib.pmcw.pim == 0x80) - goto out_unreg; - - if (check_for_io_on_path(sch, mask)) { - if (device_is_online(sch)) - device_kill_io(sch); - else { - terminate_internal_io(sch); - /* Re-start path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - } - } else { - /* trigger path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - else if (sch->lpm == mask) + if (sch->driver && sch->driver->chp_event) + if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0) goto out_unreg; - } - spin_unlock_irq(sch->lock); return 0; @@ -211,15 +147,18 @@ out_unreg: void chsc_chp_offline(struct chp_id chpid) { char dbf_txt[15]; + struct chp_link link; sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) <= 0) return; + memset(&link, 0, sizeof(struct chp_link)); + link.chpid = chpid; /* Wait until previous actions have settled. */ css_wait_for_slow_path(); - for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); + for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &link); } static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) @@ -242,67 +181,25 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) return 0; } -struct res_acc_data { - struct chp_id chpid; - u32 fla_mask; - u16 fla; -}; - -static int get_res_chpid_mask(struct chsc_ssd_info *ssd, - struct res_acc_data *data) -{ - int i; - int mask; - - for (i = 0; i < 8; i++) { - mask = 0x80 >> i; - if (!(ssd->path_mask & mask)) - continue; - if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) - continue; - if ((ssd->fla_valid_mask & mask) && - ((ssd->fla[i] & data->fla_mask) != data->fla)) - continue; - return mask; - } - return 0; -} - static int __s390_process_res_acc(struct subchannel *sch, void *data) { - int chp_mask, old_lpm; - struct res_acc_data *res_data = data; - spin_lock_irq(sch->lock); - chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); - if (chp_mask == 0) - goto out; - if (stsch(sch->schid, &sch->schib)) - goto out; - old_lpm = sch->lpm; - sch->lpm = ((sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom) - | chp_mask) & sch->opm; - if (!old_lpm && sch->lpm) - device_trigger_reprobe(sch); - else if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); -out: + if (sch->driver && sch->driver->chp_event) + sch->driver->chp_event(sch, data, CHP_ONLINE); spin_unlock_irq(sch->lock); return 0; } -static void s390_process_res_acc (struct res_acc_data *res_data) +static void s390_process_res_acc(struct chp_link *link) { char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, - res_data->chpid.id); + sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid, + link->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); - if (res_data->fla != 0) { - sprintf(dbf_txt, "fla%x", res_data->fla); + if (link->fla != 0) { + sprintf(dbf_txt, "fla%x", link->fla); CIO_TRACE_EVENT( 2, dbf_txt); } /* Wait until previous actions have settled. */ @@ -315,7 +212,7 @@ static void s390_process_res_acc (struct res_acc_data *res_data) * will we have to do. */ for_each_subchannel_staged(__s390_process_res_acc, - s390_process_res_acc_new_sch, res_data); + s390_process_res_acc_new_sch, link); } static int @@ -388,7 +285,7 @@ static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { - struct res_acc_data res_data; + struct chp_link link; struct chp_id chpid; int status; @@ -404,18 +301,18 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) chp_new(chpid); else if (!status) return; - memset(&res_data, 0, sizeof(struct res_acc_data)); - res_data.chpid = chpid; + memset(&link, 0, sizeof(struct chp_link)); + link.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { - res_data.fla = sei_area->fla; + link.fla = sei_area->fla; if ((sei_area->vf & 0xc0) == 0xc0) /* full link address */ - res_data.fla_mask = 0xffff; + link.fla_mask = 0xffff; else /* link address */ - res_data.fla_mask = 0xff00; + link.fla_mask = 0xff00; } - s390_process_res_acc(&res_data); + s390_process_res_acc(&link); } struct chp_config_data { @@ -480,17 +377,25 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) } } -void chsc_process_crw(void) +static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { struct chsc_sei_area *sei_area; + if (overflow) { + css_schedule_eval_all(); + return; + } + CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, " + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", + crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, + crw0->erc, crw0->rsid); if (!sei_page) return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ sei_area = sei_page; - CIO_TRACE_EVENT( 2, "prcss"); + CIO_TRACE_EVENT(2, "prcss"); do { memset(sei_area, 0, sizeof(*sei_area)); sei_area->request.length = 0x0010; @@ -509,114 +414,36 @@ void chsc_process_crw(void) } while (sei_area->flags & 0x80); } -static int __chp_add_new_sch(struct subchannel_id schid, void *data) -{ - struct schib schib; - - if (stsch_err(schid, &schib)) - /* We're through */ - return -ENXIO; - - /* Put it on the slow path. */ - css_schedule_eval(schid); - return 0; -} - - -static int __chp_add(struct subchannel *sch, void *data) -{ - int i, mask; - struct chp_id *chpid = data; - - 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] == chpid->id)) - break; - } - if (i==8) { - spin_unlock_irq(sch->lock); - return 0; - } - if (stsch(sch->schid, &sch->schib)) { - spin_unlock_irq(sch->lock); - css_schedule_eval(sch->schid); - return 0; - } - sch->lpm = ((sch->schib.pmcw.pim & - sch->schib.pmcw.pam & - sch->schib.pmcw.pom) - | mask) & sch->opm; - - if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - - spin_unlock_irq(sch->lock); - - return 0; -} - void chsc_chp_online(struct chp_id chpid) { char dbf_txt[15]; + struct chp_link link; sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) != 0) { + memset(&link, 0, sizeof(struct chp_link)); + link.chpid = chpid; /* Wait until previous actions have settled. */ css_wait_for_slow_path(); - for_each_subchannel_staged(__chp_add, __chp_add_new_sch, - &chpid); + for_each_subchannel_staged(__s390_process_res_acc, NULL, + &link); } } static void __s390_subchannel_vary_chpid(struct subchannel *sch, struct chp_id chpid, int on) { - int chp, old_lpm; - int mask; unsigned long flags; + struct chp_link link; + memset(&link, 0, sizeof(struct chp_link)); + link.chpid = chpid; spin_lock_irqsave(sch->lock, flags); - old_lpm = sch->lpm; - for (chp = 0; chp < 8; chp++) { - mask = 0x80 >> chp; - if (!(sch->ssd_info.path_mask & mask)) - continue; - if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) - continue; - - if (on) { - sch->opm |= mask; - sch->lpm |= mask; - if (!old_lpm) - device_trigger_reprobe(sch); - else if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - break; - } - sch->opm &= ~mask; - sch->lpm &= ~mask; - if (check_for_io_on_path(sch, mask)) { - if (device_is_online(sch)) - /* Path verification is done after killing. */ - device_kill_io(sch); - else { - /* Kill and retry internal I/O. */ - terminate_internal_io(sch); - /* Re-start path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - } - } else if (!sch->lpm) { - if (device_trigger_verify(sch) != 0) - css_schedule_eval(sch->schid); - } else if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); - break; - } + if (sch->driver && sch->driver->chp_event) + sch->driver->chp_event(sch, &link, + on ? CHP_VARY_ON : CHP_VARY_OFF); spin_unlock_irqrestore(sch->lock, flags); } @@ -656,6 +483,10 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) */ int chsc_chp_vary(struct chp_id chpid, int on) { + struct chp_link link; + + memset(&link, 0, sizeof(struct chp_link)); + link.chpid = chpid; /* Wait until previous actions have settled. */ css_wait_for_slow_path(); /* @@ -664,10 +495,10 @@ int chsc_chp_vary(struct chp_id chpid, int on) if (on) for_each_subchannel_staged(s390_subchannel_vary_chpid_on, - __s390_vary_chpid_on, &chpid); + __s390_vary_chpid_on, &link); else for_each_subchannel_staged(s390_subchannel_vary_chpid_off, - NULL, &chpid); + NULL, &link); return 0; } @@ -797,23 +628,33 @@ chsc_secm(struct channel_subsystem *css, int enable) return ret; } -int chsc_determine_channel_path_description(struct chp_id chpid, - struct channel_path_desc *desc) +int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, + int c, int m, + struct chsc_response_struct *resp) { int ccode, ret; struct { struct chsc_header request; - u32 : 24; + u32 : 2; + u32 m : 1; + u32 c : 1; + u32 fmt : 4; + u32 cssid : 8; + u32 : 4; + u32 rfmt : 4; u32 first_chpid : 8; u32 : 24; u32 last_chpid : 8; u32 zeroes1; struct chsc_header response; - u32 zeroes2; - struct channel_path_desc desc; + u8 data[PAGE_SIZE - 20]; } __attribute__ ((packed)) *scpd_area; + if ((rfmt == 1) && !css_general_characteristics.fcs) + return -EINVAL; + if ((rfmt == 2) && !css_general_characteristics.cib) + return -EINVAL; scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scpd_area) return -ENOMEM; @@ -821,8 +662,13 @@ int chsc_determine_channel_path_description(struct chp_id chpid, scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; + scpd_area->cssid = chpid.cssid; scpd_area->first_chpid = chpid.id; scpd_area->last_chpid = chpid.id; + scpd_area->m = m; + scpd_area->c = c; + scpd_area->fmt = fmt; + scpd_area->rfmt = rfmt; ccode = chsc(scpd_area); if (ccode > 0) { @@ -833,8 +679,7 @@ int chsc_determine_channel_path_description(struct chp_id chpid, ret = chsc_error_from_response(scpd_area->response.code); if (ret == 0) /* Success. */ - memcpy(desc, &scpd_area->desc, - sizeof(struct channel_path_desc)); + memcpy(resp, &scpd_area->response, scpd_area->response.length); else CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", scpd_area->response.code); @@ -842,6 +687,25 @@ out: free_page((unsigned long)scpd_area); return ret; } +EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); + +int chsc_determine_base_channel_path_desc(struct chp_id chpid, + struct channel_path_desc *desc) +{ + struct chsc_response_struct *chsc_resp; + int ret; + + chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); + if (!chsc_resp) + return -ENOMEM; + ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); + if (ret) + goto out_free; + memcpy(desc, &chsc_resp->data, chsc_resp->length); +out_free: + kfree(chsc_resp); + return ret; +} static void chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, @@ -937,15 +801,23 @@ out: int __init chsc_alloc_sei_area(void) { + int ret; + sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sei_page) + if (!sei_page) { CIO_MSG_EVENT(0, "Can't allocate page for processing of " "chsc machine checks!\n"); - return (sei_page ? 0 : -ENOMEM); + return -ENOMEM; + } + ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw); + if (ret) + kfree(sei_page); + return ret; } void __init chsc_free_sei_area(void) { + s390_unregister_crw_handler(CRW_RSC_CSS); kfree(sei_page); } @@ -1043,3 +915,52 @@ exit: EXPORT_SYMBOL_GPL(css_general_characteristics); EXPORT_SYMBOL_GPL(css_chsc_characteristics); + +int chsc_sstpc(void *page, unsigned int op, u16 ctrl) +{ + struct { + struct chsc_header request; + unsigned int rsvd0; + unsigned int op : 8; + unsigned int rsvd1 : 8; + unsigned int ctrl : 16; + unsigned int rsvd2[5]; + struct chsc_header response; + unsigned int rsvd3[7]; + } __attribute__ ((packed)) *rr; + int rc; + + memset(page, 0, PAGE_SIZE); + rr = page; + rr->request.length = 0x0020; + rr->request.code = 0x0033; + rr->op = op; + rr->ctrl = ctrl; + rc = chsc(rr); + if (rc) + return -EIO; + rc = (rr->response.code == 0x0001) ? 0 : -EIO; + return rc; +} + +int chsc_sstpi(void *page, void *result, size_t size) +{ + struct { + struct chsc_header request; + unsigned int rsvd0[3]; + struct chsc_header response; + char data[size]; + } __attribute__ ((packed)) *rr; + int rc; + + memset(page, 0, PAGE_SIZE); + rr = page; + rr->request.length = 0x0010; + rr->request.code = 0x0038; + rc = chsc(rr); + if (rc) + return -EIO; + memcpy(result, &rr->data, size); + return (rr->response.code == 0x0001) ? 0 : -EIO; +} + diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index d1f5db1e69b..fb6c4d6c45b 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -4,7 +4,8 @@ #include <linux/types.h> #include <linux/device.h> #include <asm/chpid.h> -#include "schid.h" +#include <asm/chsc.h> +#include <asm/schid.h> #define CHSC_SDA_OC_MSS 0x2 @@ -36,14 +37,15 @@ struct channel_path_desc { struct channel_path; -extern void chsc_process_crw(void); - struct css_general_char { - u64 : 41; + u64 : 12; + u32 dynio : 1; /* bit 12 */ + u32 : 28; u32 aif : 1; /* bit 41 */ u32 : 3; u32 mcss : 1; /* bit 45 */ - u32 : 2; + u32 fcs : 1; /* bit 46 */ + u32 : 1; u32 ext_mb : 1; /* bit 48 */ u32 : 7; u32 aif_tdd : 1; /* bit 56 */ @@ -51,7 +53,11 @@ struct css_general_char { u32 qebsm : 1; /* bit 58 */ u32 : 8; u32 aif_osa : 1; /* bit 67 */ - u32 : 28; + u32 : 14; + u32 cib : 1; /* bit 82 */ + u32 : 5; + u32 fcx : 1; /* bit 88 */ + u32 : 7; }__attribute__((packed)); struct css_chsc_char { @@ -78,7 +84,6 @@ struct chsc_ssd_info { extern int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); -extern int css_characteristics_avail; extern int chsc_alloc_sei_area(void); extern void chsc_free_sei_area(void); @@ -87,8 +92,11 @@ struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); int chsc_chp_vary(struct chp_id chpid, int on); -int chsc_determine_channel_path_description(struct chp_id chpid, - struct channel_path_desc *desc); +int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, + int c, int m, + struct chsc_response_struct *resp); +int chsc_determine_base_channel_path_desc(struct chp_id chpid, + struct channel_path_desc *desc); void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c new file mode 100644 index 00000000000..91ca87aa9f9 --- /dev/null +++ b/drivers/s390/cio/chsc_sch.c @@ -0,0 +1,820 @@ +/* + * Driver for s390 chsc subchannels + * + * Copyright IBM Corp. 2008 + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> + +#include <asm/cio.h> +#include <asm/chsc.h> +#include <asm/isc.h> + +#include "cio.h" +#include "cio_debug.h" +#include "css.h" +#include "chsc_sch.h" +#include "ioasm.h" + +static debug_info_t *chsc_debug_msg_id; +static debug_info_t *chsc_debug_log_id; + +#define CHSC_MSG(imp, args...) do { \ + debug_sprintf_event(chsc_debug_msg_id, imp , ##args); \ + } while (0) + +#define CHSC_LOG(imp, txt) do { \ + debug_text_event(chsc_debug_log_id, imp , txt); \ + } while (0) + +static void CHSC_LOG_HEX(int level, void *data, int length) +{ + while (length > 0) { + debug_event(chsc_debug_log_id, level, data, length); + length -= chsc_debug_log_id->buf_size; + data += chsc_debug_log_id->buf_size; + } +} + +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("driver for s390 chsc subchannels"); +MODULE_LICENSE("GPL"); + +static void chsc_subchannel_irq(struct subchannel *sch) +{ + struct chsc_private *private = sch->private; + struct chsc_request *request = private->request; + struct irb *irb = (struct irb *)__LC_IRB; + + CHSC_LOG(4, "irb"); + CHSC_LOG_HEX(4, irb, sizeof(*irb)); + /* Copy irb to provided request and set done. */ + if (!request) { + CHSC_MSG(0, "Interrupt on sch 0.%x.%04x with no request\n", + sch->schid.ssid, sch->schid.sch_no); + return; + } + private->request = NULL; + memcpy(&request->irb, irb, sizeof(*irb)); + stsch(sch->schid, &sch->schib); + complete(&request->completion); + put_device(&sch->dev); +} + +static int chsc_subchannel_probe(struct subchannel *sch) +{ + struct chsc_private *private; + int ret; + + CHSC_MSG(6, "Detected chsc subchannel 0.%x.%04x\n", + sch->schid.ssid, sch->schid.sch_no); + sch->isc = CHSC_SCH_ISC; + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch); + if (ret) { + CHSC_MSG(0, "Failed to enable 0.%x.%04x: %d\n", + sch->schid.ssid, sch->schid.sch_no, ret); + kfree(private); + } else { + sch->private = private; + if (sch->dev.uevent_suppress) { + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); + } + } + return ret; +} + +static int chsc_subchannel_remove(struct subchannel *sch) +{ + struct chsc_private *private; + + cio_disable_subchannel(sch); + private = sch->private; + sch->private = NULL; + if (private->request) { + complete(&private->request->completion); + put_device(&sch->dev); + } + kfree(private); + return 0; +} + +static void chsc_subchannel_shutdown(struct subchannel *sch) +{ + cio_disable_subchannel(sch); +} + +static struct css_device_id chsc_subchannel_ids[] = { + { .match_flags = 0x1, .type =SUBCHANNEL_TYPE_CHSC, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(css, chsc_subchannel_ids); + +static struct css_driver chsc_subchannel_driver = { + .owner = THIS_MODULE, + .subchannel_type = chsc_subchannel_ids, + .irq = chsc_subchannel_irq, + .probe = chsc_subchannel_probe, + .remove = chsc_subchannel_remove, + .shutdown = chsc_subchannel_shutdown, + .name = "chsc_subchannel", +}; + +static int __init chsc_init_dbfs(void) +{ + chsc_debug_msg_id = debug_register("chsc_msg", 16, 1, + 16 * sizeof(long)); + if (!chsc_debug_msg_id) + goto out; + debug_register_view(chsc_debug_msg_id, &debug_sprintf_view); + debug_set_level(chsc_debug_msg_id, 2); + chsc_debug_log_id = debug_register("chsc_log", 16, 1, 16); + if (!chsc_debug_log_id) + goto out; + debug_register_view(chsc_debug_log_id, &debug_hex_ascii_view); + debug_set_level(chsc_debug_log_id, 2); + return 0; +out: + if (chsc_debug_msg_id) + debug_unregister(chsc_debug_msg_id); + return -ENOMEM; +} + +static void chsc_remove_dbfs(void) +{ + debug_unregister(chsc_debug_log_id); + debug_unregister(chsc_debug_msg_id); +} + +static int __init chsc_init_sch_driver(void) +{ + return css_driver_register(&chsc_subchannel_driver); +} + +static void chsc_cleanup_sch_driver(void) +{ + css_driver_unregister(&chsc_subchannel_driver); +} + +static DEFINE_SPINLOCK(chsc_lock); + +static int chsc_subchannel_match_next_free(struct device *dev, void *data) +{ + struct subchannel *sch = to_subchannel(dev); + + return sch->schib.pmcw.ena && !scsw_fctl(&sch->schib.scsw); +} + +static struct subchannel *chsc_get_next_subchannel(struct subchannel *sch) +{ + struct device *dev; + + dev = driver_find_device(&chsc_subchannel_driver.drv, + sch ? &sch->dev : NULL, NULL, + chsc_subchannel_match_next_free); + return dev ? to_subchannel(dev) : NULL; +} + +/** + * chsc_async() - try to start a chsc request asynchronously + * @chsc_area: request to be started + * @request: request structure to associate + * + * Tries to start a chsc request on one of the existing chsc subchannels. + * Returns: + * %0 if the request was performed synchronously + * %-EINPROGRESS if the request was successfully started + * %-EBUSY if all chsc subchannels are busy + * %-ENODEV if no chsc subchannels are available + * Context: + * interrupts disabled, chsc_lock held + */ +static int chsc_async(struct chsc_async_area *chsc_area, + struct chsc_request *request) +{ + int cc; + struct chsc_private *private; + struct subchannel *sch = NULL; + int ret = -ENODEV; + char dbf[10]; + + chsc_area->header.key = PAGE_DEFAULT_KEY; + while ((sch = chsc_get_next_subchannel(sch))) { + spin_lock(sch->lock); + private = sch->private; + if (private->request) { + spin_unlock(sch->lock); + ret = -EBUSY; + continue; + } + chsc_area->header.sid = sch->schid; + CHSC_LOG(2, "schid"); + CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid)); + cc = chsc(chsc_area); + sprintf(dbf, "cc:%d", cc); + CHSC_LOG(2, dbf); + switch (cc) { + case 0: + ret = 0; + break; + case 1: + sch->schib.scsw.cmd.fctl |= SCSW_FCTL_START_FUNC; + ret = -EINPROGRESS; + private->request = request; + break; + case 2: + ret = -EBUSY; + break; + default: + ret = -ENODEV; + } + spin_unlock(sch->lock); + CHSC_MSG(2, "chsc on 0.%x.%04x returned cc=%d\n", + sch->schid.ssid, sch->schid.sch_no, cc); + if (ret == -EINPROGRESS) + return -EINPROGRESS; + put_device(&sch->dev); + if (ret == 0) + return 0; + } + return ret; +} + +static void chsc_log_command(struct chsc_async_area *chsc_area) +{ + char dbf[10]; + + sprintf(dbf, "CHSC:%x", chsc_area->header.code); + CHSC_LOG(0, dbf); + CHSC_LOG_HEX(0, chsc_area, 32); +} + +static int chsc_examine_irb(struct chsc_request *request) +{ + int backed_up; + + if (!scsw_stctl(&request->irb.scsw) & SCSW_STCTL_STATUS_PEND) + return -EIO; + backed_up = scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHAIN_CHECK; + request->irb.scsw.cmd.cstat &= ~SCHN_STAT_CHAIN_CHECK; + if (scsw_cstat(&request->irb.scsw) == 0) + return 0; + if (!backed_up) + return 0; + if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROG_CHECK) + return -EIO; + if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_PROT_CHECK) + return -EPERM; + if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_DATA_CHK) + return -EAGAIN; + if (scsw_cstat(&request->irb.scsw) & SCHN_STAT_CHN_CTRL_CHK) + return -EAGAIN; + return -EIO; +} + +static int chsc_ioctl_start(void __user *user_area) +{ + struct chsc_request *request; + struct chsc_async_area *chsc_area; + int ret; + char dbf[10]; + + if (!css_general_characteristics.dynio) + /* It makes no sense to try. */ + return -EOPNOTSUPP; + chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!chsc_area) + return -ENOMEM; + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) { + ret = -ENOMEM; + goto out_free; + } + init_completion(&request->completion); + if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { + ret = -EFAULT; + goto out_free; + } + chsc_log_command(chsc_area); + spin_lock_irq(&chsc_lock); + ret = chsc_async(chsc_area, request); + spin_unlock_irq(&chsc_lock); + if (ret == -EINPROGRESS) { + wait_for_completion(&request->completion); + ret = chsc_examine_irb(request); + } + /* copy area back to user */ + if (!ret) + if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) + ret = -EFAULT; +out_free: + sprintf(dbf, "ret:%d", ret); + CHSC_LOG(0, dbf); + kfree(request); + free_page((unsigned long)chsc_area); + return ret; +} + +static int chsc_ioctl_info_channel_path(void __user *user_cd) +{ + struct chsc_chp_cd *cd; + int ret, ccode; + struct { + struct chsc_header request; + u32 : 2; + u32 m : 1; + u32 : 1; + u32 fmt1 : 4; + u32 cssid : 8; + u32 : 8; + u32 first_chpid : 8; + u32 : 24; + u32 last_chpid : 8; + u32 : 32; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scpcd_area; + + scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpcd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(cd, user_cd, sizeof(*cd))) { + ret = -EFAULT; + goto out_free; + } + scpcd_area->request.length = 0x0010; + scpcd_area->request.code = 0x0028; + scpcd_area->m = cd->m; + scpcd_area->fmt1 = cd->fmt; + scpcd_area->cssid = cd->chpid.cssid; + scpcd_area->first_chpid = cd->chpid.id; + scpcd_area->last_chpid = cd->chpid.id; + + ccode = chsc(scpcd_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (scpcd_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "scpcd: response code=%x\n", + scpcd_area->response.code); + goto out_free; + } + memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length); + if (copy_to_user(user_cd, cd, sizeof(*cd))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(cd); + free_page((unsigned long)scpcd_area); + return ret; +} + +static int chsc_ioctl_info_cu(void __user *user_cd) +{ + struct chsc_cu_cd *cd; + int ret, ccode; + struct { + struct chsc_header request; + u32 : 2; + u32 m : 1; + u32 : 1; + u32 fmt1 : 4; + u32 cssid : 8; + u32 : 8; + u32 first_cun : 8; + u32 : 24; + u32 last_cun : 8; + u32 : 32; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *scucd_area; + + scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scucd_area) + return -ENOMEM; + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(cd, user_cd, sizeof(*cd))) { + ret = -EFAULT; + goto out_free; + } + scucd_area->request.length = 0x0010; + scucd_area->request.code = 0x0028; + scucd_area->m = cd->m; + scucd_area->fmt1 = cd->fmt; + scucd_area->cssid = cd->cssid; + scucd_area->first_cun = cd->cun; + scucd_area->last_cun = cd->cun; + + ccode = chsc(scucd_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (scucd_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "scucd: response code=%x\n", + scucd_area->response.code); + goto out_free; + } + memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length); + if (copy_to_user(user_cd, cd, sizeof(*cd))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(cd); + free_page((unsigned long)scucd_area); + return ret; +} + +static int chsc_ioctl_info_sch_cu(void __user *user_cud) +{ + struct chsc_sch_cud *cud; + int ret, ccode; + struct { + struct chsc_header request; + u32 : 2; + u32 m : 1; + u32 : 5; + u32 fmt1 : 4; + u32 : 2; + u32 ssid : 2; + u32 first_sch : 16; + u32 : 8; + u32 cssid : 8; + u32 last_sch : 16; + u32 : 32; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sscud_area; + + sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sscud_area) + return -ENOMEM; + cud = kzalloc(sizeof(*cud), GFP_KERNEL); + if (!cud) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(cud, user_cud, sizeof(*cud))) { + ret = -EFAULT; + goto out_free; + } + sscud_area->request.length = 0x0010; + sscud_area->request.code = 0x0006; + sscud_area->m = cud->schid.m; + sscud_area->fmt1 = cud->fmt; + sscud_area->ssid = cud->schid.ssid; + sscud_area->first_sch = cud->schid.sch_no; + sscud_area->cssid = cud->schid.cssid; + sscud_area->last_sch = cud->schid.sch_no; + + ccode = chsc(sscud_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (sscud_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "sscud: response code=%x\n", + sscud_area->response.code); + goto out_free; + } + memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length); + if (copy_to_user(user_cud, cud, sizeof(*cud))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(cud); + free_page((unsigned long)sscud_area); + return ret; +} + +static int chsc_ioctl_conf_info(void __user *user_ci) +{ + struct chsc_conf_info *ci; + int ret, ccode; + struct { + struct chsc_header request; + u32 : 2; + u32 m : 1; + u32 : 1; + u32 fmt1 : 4; + u32 cssid : 8; + u32 : 6; + u32 ssid : 2; + u32 : 8; + u64 : 64; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; + } __attribute__ ((packed)) *sci_area; + + sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sci_area) + return -ENOMEM; + ci = kzalloc(sizeof(*ci), GFP_KERNEL); + if (!ci) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(ci, user_ci, sizeof(*ci))) { + ret = -EFAULT; + goto out_free; + } + sci_area->request.length = 0x0010; + sci_area->request.code = 0x0012; + sci_area->m = ci->id.m; + sci_area->fmt1 = ci->fmt; + sci_area->cssid = ci->id.cssid; + sci_area->ssid = ci->id.ssid; + + ccode = chsc(sci_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (sci_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "sci: response code=%x\n", + sci_area->response.code); + goto out_free; + } + memcpy(&ci->scid, &sci_area->response, sci_area->response.length); + if (copy_to_user(user_ci, ci, sizeof(*ci))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(ci); + free_page((unsigned long)sci_area); + return ret; +} + +static int chsc_ioctl_conf_comp_list(void __user *user_ccl) +{ + struct chsc_comp_list *ccl; + int ret, ccode; + struct { + struct chsc_header request; + u32 ctype : 8; + u32 : 4; + u32 fmt : 4; + u32 : 16; + u64 : 64; + u32 list_parm[2]; + u64 : 64; + struct chsc_header response; + u8 data[PAGE_SIZE - 36]; + } __attribute__ ((packed)) *sccl_area; + struct { + u32 m : 1; + u32 : 31; + u32 cssid : 8; + u32 : 16; + u32 chpid : 8; + } __attribute__ ((packed)) *chpid_parm; + struct { + u32 f_cssid : 8; + u32 l_cssid : 8; + u32 : 16; + u32 res; + } __attribute__ ((packed)) *cssids_parm; + + sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccl_area) + return -ENOMEM; + ccl = kzalloc(sizeof(*ccl), GFP_KERNEL); + if (!ccl) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) { + ret = -EFAULT; + goto out_free; + } + sccl_area->request.length = 0x0020; + sccl_area->request.code = 0x0030; + sccl_area->fmt = ccl->req.fmt; + sccl_area->ctype = ccl->req.ctype; + switch (sccl_area->ctype) { + case CCL_CU_ON_CHP: + case CCL_IOP_CHP: + chpid_parm = (void *)&sccl_area->list_parm; + chpid_parm->m = ccl->req.chpid.m; + chpid_parm->cssid = ccl->req.chpid.chp.cssid; + chpid_parm->chpid = ccl->req.chpid.chp.id; + break; + case CCL_CSS_IMG: + case CCL_CSS_IMG_CONF_CHAR: + cssids_parm = (void *)&sccl_area->list_parm; + cssids_parm->f_cssid = ccl->req.cssids.f_cssid; + cssids_parm->l_cssid = ccl->req.cssids.l_cssid; + break; + } + ccode = chsc(sccl_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (sccl_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "sccl: response code=%x\n", + sccl_area->response.code); + goto out_free; + } + memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length); + if (copy_to_user(user_ccl, ccl, sizeof(*ccl))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(ccl); + free_page((unsigned long)sccl_area); + return ret; +} + +static int chsc_ioctl_chpd(void __user *user_chpd) +{ + struct chsc_cpd_info *chpd; + int ret; + + chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); + if (!chpd) + return -ENOMEM; + if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) { + ret = -EFAULT; + goto out_free; + } + ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt, + chpd->rfmt, chpd->c, chpd->m, + &chpd->chpdb); + if (ret) + goto out_free; + if (copy_to_user(user_chpd, chpd, sizeof(*chpd))) + ret = -EFAULT; +out_free: + kfree(chpd); + return ret; +} + +static int chsc_ioctl_dcal(void __user *user_dcal) +{ + struct chsc_dcal *dcal; + int ret, ccode; + struct { + struct chsc_header request; + u32 atype : 8; + u32 : 4; + u32 fmt : 4; + u32 : 16; + u32 res0[2]; + u32 list_parm[2]; + u32 res1[2]; + struct chsc_header response; + u8 data[PAGE_SIZE - 36]; + } __attribute__ ((packed)) *sdcal_area; + + sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sdcal_area) + return -ENOMEM; + dcal = kzalloc(sizeof(*dcal), GFP_KERNEL); + if (!dcal) { + ret = -ENOMEM; + goto out_free; + } + if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) { + ret = -EFAULT; + goto out_free; + } + sdcal_area->request.length = 0x0020; + sdcal_area->request.code = 0x0034; + sdcal_area->atype = dcal->req.atype; + sdcal_area->fmt = dcal->req.fmt; + memcpy(&sdcal_area->list_parm, &dcal->req.list_parm, + sizeof(sdcal_area->list_parm)); + + ccode = chsc(sdcal_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (sdcal_area->response.code != 0x0001) { + ret = -EIO; + CHSC_MSG(0, "sdcal: response code=%x\n", + sdcal_area->response.code); + goto out_free; + } + memcpy(&dcal->sdcal, &sdcal_area->response, + sdcal_area->response.length); + if (copy_to_user(user_dcal, dcal, sizeof(*dcal))) + ret = -EFAULT; + else + ret = 0; +out_free: + kfree(dcal); + free_page((unsigned long)sdcal_area); + return ret; +} + +static long chsc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + CHSC_MSG(2, "chsc_ioctl called, cmd=%x\n", cmd); + switch (cmd) { + case CHSC_START: + return chsc_ioctl_start((void __user *)arg); + case CHSC_INFO_CHANNEL_PATH: + return chsc_ioctl_info_channel_path((void __user *)arg); + case CHSC_INFO_CU: + return chsc_ioctl_info_cu((void __user *)arg); + case CHSC_INFO_SCH_CU: + return chsc_ioctl_info_sch_cu((void __user *)arg); + case CHSC_INFO_CI: + return chsc_ioctl_conf_info((void __user *)arg); + case CHSC_INFO_CCL: + return chsc_ioctl_conf_comp_list((void __user *)arg); + case CHSC_INFO_CPD: + return chsc_ioctl_chpd((void __user *)arg); + case CHSC_INFO_DCAL: + return chsc_ioctl_dcal((void __user *)arg); + default: /* unknown ioctl number */ + return -ENOIOCTLCMD; + } +} + +static const struct file_operations chsc_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = chsc_ioctl, + .compat_ioctl = chsc_ioctl, +}; + +static struct miscdevice chsc_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "chsc", + .fops = &chsc_fops, +}; + +static int __init chsc_misc_init(void) +{ + return misc_register(&chsc_misc_device); +} + +static void chsc_misc_cleanup(void) +{ + misc_deregister(&chsc_misc_device); +} + +static int __init chsc_sch_init(void) +{ + int ret; + + ret = chsc_init_dbfs(); + if (ret) + return ret; + isc_register(CHSC_SCH_ISC); + ret = chsc_init_sch_driver(); + if (ret) + goto out_dbf; + ret = chsc_misc_init(); + if (ret) + goto out_driver; + return ret; +out_driver: + chsc_cleanup_sch_driver(); +out_dbf: + isc_unregister(CHSC_SCH_ISC); + chsc_remove_dbfs(); + return ret; +} + +static void __exit chsc_sch_exit(void) +{ + chsc_misc_cleanup(); + chsc_cleanup_sch_driver(); + isc_unregister(CHSC_SCH_ISC); + chsc_remove_dbfs(); +} + +module_init(chsc_sch_init); +module_exit(chsc_sch_exit); diff --git a/drivers/s390/cio/chsc_sch.h b/drivers/s390/cio/chsc_sch.h new file mode 100644 index 00000000000..589ebfad6aa --- /dev/null +++ b/drivers/s390/cio/chsc_sch.h @@ -0,0 +1,13 @@ +#ifndef _CHSC_SCH_H +#define _CHSC_SCH_H + +struct chsc_request { + struct completion completion; + struct irb irb; +}; + +struct chsc_private { + struct chsc_request *request; +}; + +#endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index b32d7eb3d81..33bff8fec7d 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -2,7 +2,7 @@ * drivers/s390/cio/cio.c * S/390 common I/O routines -- low level i/o calls * - * Copyright (C) IBM Corp. 1999,2006 + * Copyright IBM Corp. 1999,2008 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) @@ -24,7 +24,9 @@ #include <asm/ipl.h> #include <asm/chpid.h> #include <asm/airq.h> +#include <asm/isc.h> #include <asm/cpu.h> +#include <asm/fcx.h> #include "cio.h" #include "css.h" #include "chsc.h" @@ -72,7 +74,6 @@ out_unregister: debug_unregister(cio_debug_trace_id); if (cio_debug_crw_id) debug_unregister(cio_debug_crw_id); - printk(KERN_WARNING"cio: could not initialize debugging\n"); return -1; } @@ -128,7 +129,7 @@ cio_tpi(void) local_bh_disable(); irq_enter (); spin_lock(sch->lock); - memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw)); + memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw)); if (sch->driver && sch->driver->irq) sch->driver->irq(sch); spin_unlock(sch->lock); @@ -167,30 +168,30 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ { char dbf_txt[15]; int ccode; - struct orb *orb; + union orb *orb; CIO_TRACE_EVENT(4, "stIO"); CIO_TRACE_EVENT(4, sch->dev.bus_id); orb = &to_io_private(sch)->orb; /* sch is always under 2G. */ - orb->intparm = (u32)(addr_t)sch; - orb->fmt = 1; + orb->cmd.intparm = (u32)(addr_t)sch; + orb->cmd.fmt = 1; - orb->pfch = sch->options.prefetch == 0; - orb->spnd = sch->options.suspend; - orb->ssic = sch->options.suspend && sch->options.inter; - orb->lpm = (lpm != 0) ? lpm : sch->lpm; + orb->cmd.pfch = sch->options.prefetch == 0; + orb->cmd.spnd = sch->options.suspend; + orb->cmd.ssic = sch->options.suspend && sch->options.inter; + orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ - orb->c64 = 1; - orb->i2k = 0; + orb->cmd.c64 = 1; + orb->cmd.i2k = 0; #endif - orb->key = key >> 4; + orb->cmd.key = key >> 4; /* issue "Start Subchannel" */ - orb->cpa = (__u32) __pa(cpa); + orb->cmd.cpa = (__u32) __pa(cpa); ccode = ssch(sch->schid, orb); /* process condition code */ @@ -202,7 +203,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */ /* * initialize device status information */ - sch->schib.scsw.actl |= SCSW_ACTL_START_PEND; + sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ @@ -237,7 +238,7 @@ cio_resume (struct subchannel *sch) switch (ccode) { case 0: - sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND; + sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND; return 0; case 1: return -EBUSY; @@ -277,7 +278,7 @@ cio_halt(struct subchannel *sch) switch (ccode) { case 0: - sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND; + sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND; return 0; case 1: /* status pending */ case 2: /* busy */ @@ -312,7 +313,7 @@ cio_clear(struct subchannel *sch) switch (ccode) { case 0: - sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND; + sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND; return 0; default: /* device not operational */ return -ENODEV; @@ -387,8 +388,10 @@ cio_modify (struct subchannel *sch) return ret; } -/* - * Enable subchannel. +/** + * cio_enable_subchannel - enable a subchannel. + * @sch: subchannel to be enabled + * @intparm: interruption parameter to set */ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) { @@ -434,12 +437,13 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm) CIO_TRACE_EVENT (2, dbf_txt); return ret; } +EXPORT_SYMBOL_GPL(cio_enable_subchannel); -/* - * Disable subchannel. +/** + * cio_disable_subchannel - disable a subchannel. + * @sch: subchannel to disable */ -int -cio_disable_subchannel (struct subchannel *sch) +int cio_disable_subchannel(struct subchannel *sch) { char dbf_txt[15]; int ccode; @@ -455,7 +459,7 @@ cio_disable_subchannel (struct subchannel *sch) if (ccode == 3) /* Not operational. */ return -ENODEV; - if (sch->schib.scsw.actl != 0) + if (scsw_actl(&sch->schib.scsw) != 0) /* * the disable function must not be called while there are * requests pending for completion ! @@ -484,6 +488,7 @@ cio_disable_subchannel (struct subchannel *sch) CIO_TRACE_EVENT (2, dbf_txt); return ret; } +EXPORT_SYMBOL_GPL(cio_disable_subchannel); int cio_create_sch_lock(struct subchannel *sch) { @@ -494,27 +499,61 @@ int cio_create_sch_lock(struct subchannel *sch) return 0; } -/* - * cio_validate_subchannel() +static int cio_check_devno_blacklisted(struct subchannel *sch) +{ + if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) { + /* + * This device must not be known to Linux. So we simply + * say that there is no device and return ENODEV. + */ + CIO_MSG_EVENT(6, "Blacklisted device detected " + "at devno %04X, subchannel set %x\n", + sch->schib.pmcw.dev, sch->schid.ssid); + return -ENODEV; + } + return 0; +} + +static int cio_validate_io_subchannel(struct subchannel *sch) +{ + /* Initialization for io subchannels. */ + if (!css_sch_is_valid(&sch->schib)) + return -ENODEV; + + /* Devno is valid. */ + return cio_check_devno_blacklisted(sch); +} + +static int cio_validate_msg_subchannel(struct subchannel *sch) +{ + /* Initialization for message subchannels. */ + if (!css_sch_is_valid(&sch->schib)) + return -ENODEV; + + /* Devno is valid. */ + return cio_check_devno_blacklisted(sch); +} + +/** + * cio_validate_subchannel - basic validation of subchannel + * @sch: subchannel structure to be filled out + * @schid: subchannel id * * Find out subchannel type and initialize struct subchannel. * Return codes: - * SUBCHANNEL_TYPE_IO for a normal io subchannel - * SUBCHANNEL_TYPE_CHSC for a chsc subchannel - * SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel - * SUBCHANNEL_TYPE_ADM for a adm(?) subchannel + * 0 on success * -ENXIO for non-defined subchannels - * -ENODEV for subchannels with invalid device number or blacklisted devices + * -ENODEV for invalid subchannels or blacklisted devices + * -EIO for subchannels in an invalid subchannel set */ -int -cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) +int 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); + sprintf(dbf_txt, "valsch%x", schid.sch_no); + CIO_TRACE_EVENT(4, dbf_txt); /* Nuke all fields. */ memset(sch, 0, sizeof(struct subchannel)); @@ -546,67 +585,21 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) /* Copy subchannel type from path management control word. */ sch->st = sch->schib.pmcw.st; - /* - * ... just being curious we check for non I/O subchannels - */ - if (sch->st != 0) { - CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports " - "non-I/O subchannel type %04X\n", - sch->schid.ssid, sch->schid.sch_no, sch->st); - /* We stop here for non-io subchannels. */ - err = sch->st; - goto out; - } - - /* Initialization for io subchannels. */ - if (!css_sch_is_valid(&sch->schib)) { - err = -ENODEV; - goto out; + switch (sch->st) { + case SUBCHANNEL_TYPE_IO: + err = cio_validate_io_subchannel(sch); + break; + case SUBCHANNEL_TYPE_MSG: + err = cio_validate_msg_subchannel(sch); + break; + default: + err = 0; } - - /* Devno is valid. */ - if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { - /* - * This device must not be known to Linux. So we simply - * say that there is no device and return ENODEV. - */ - CIO_MSG_EVENT(6, "Blacklisted device detected " - "at devno %04X, subchannel set %x\n", - sch->schib.pmcw.dev, sch->schid.ssid); - err = -ENODEV; + if (err) goto out; - } - if (cio_is_console(sch->schid)) { - sch->opm = 0xff; - sch->isc = 1; - } else { - sch->opm = chp_get_sch_opm(sch); - sch->isc = 3; - } - sch->lpm = sch->schib.pmcw.pam & sch->opm; - - CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X " - "- PIM = %02X, PAM = %02X, POM = %02X\n", - sch->schib.pmcw.dev, sch->schid.ssid, - sch->schid.sch_no, sch->schib.pmcw.pim, - sch->schib.pmcw.pam, sch->schib.pmcw.pom); - /* - * We now have to initially ... - * ... enable "concurrent sense" - * ... enable "multipath mode" if more than one - * CHPID is available. This is done regardless - * whether multiple paths are available for us. - */ - sch->schib.pmcw.csense = 1; /* concurrent sense */ - sch->schib.pmcw.ena = 0; - if ((sch->lpm & (sch->lpm - 1)) != 0) - sch->schib.pmcw.mp = 1; /* multipath mode */ - /* clean up possible residual cmf stuff */ - sch->schib.pmcw.mme = 0; - sch->schib.pmcw.mbfc = 0; - sch->schib.pmcw.mbi = 0; - sch->schib.mba = 0; + CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n", + sch->schid.ssid, sch->schid.sch_no, sch->st); return 0; out: if (!cio_is_console(schid)) @@ -647,7 +640,7 @@ do_IRQ (struct pt_regs *regs) */ if (tpi_info->adapter_IO == 1 && tpi_info->int_type == IO_INTERRUPT_TYPE) { - do_adapter_IO(); + do_adapter_IO(tpi_info->isc); continue; } sch = (struct subchannel *)(unsigned long)tpi_info->intparm; @@ -706,9 +699,9 @@ void wait_cons_dev(void) if (!console_subchannel_in_use) return; - /* disable all but isc 1 (console device) */ + /* disable all but the console isc */ __ctl_store (save_cr6, 6, 6); - cr6 = 0x40000000; + cr6 = 1UL << (31 - CONSOLE_ISC); __ctl_load (cr6, 6, 6); do { @@ -716,7 +709,7 @@ void wait_cons_dev(void) if (!cio_tpi()) cpu_relax(); spin_lock(console_subchannel.lock); - } while (console_subchannel.schib.scsw.actl != 0); + } while (console_subchannel.schib.scsw.cmd.actl != 0); /* * restore previous isc value */ @@ -761,7 +754,6 @@ cio_get_console_sch_no(void) /* unlike in 2.4, we cannot autoprobe here, since * the channel subsystem is not fully initialized. * With some luck, the HWC console can take over */ - printk(KERN_WARNING "cio: No ccw console found!\n"); return -1; } return console_irq; @@ -778,6 +770,7 @@ cio_probe_console(void) sch_no = cio_get_console_sch_no(); if (sch_no == -1) { console_subchannel_in_use = 0; + printk(KERN_WARNING "cio: No ccw console found!\n"); return ERR_PTR(-ENODEV); } memset(&console_subchannel, 0, sizeof(struct subchannel)); @@ -790,15 +783,15 @@ cio_probe_console(void) } /* - * enable console I/O-interrupt subclass 1 + * enable console I/O-interrupt subclass */ - ctl_set_bit(6, 30); - console_subchannel.isc = 1; - console_subchannel.schib.pmcw.isc = 1; + isc_register(CONSOLE_ISC); + console_subchannel.schib.pmcw.isc = CONSOLE_ISC; console_subchannel.schib.pmcw.intparm = (u32)(addr_t)&console_subchannel; ret = cio_modify(&console_subchannel); if (ret) { + isc_unregister(CONSOLE_ISC); console_subchannel_in_use = 0; return ERR_PTR(ret); } @@ -810,7 +803,7 @@ cio_release_console(void) { console_subchannel.schib.pmcw.intparm = 0; cio_modify(&console_subchannel); - ctl_clear_bit(6, 24); + isc_unregister(CONSOLE_ISC); console_subchannel_in_use = 0; } @@ -864,7 +857,7 @@ static void udelay_reset(unsigned long usecs) } static int -__clear_subchannel_easy(struct subchannel_id schid) +__clear_io_subchannel_easy(struct subchannel_id schid) { int retry; @@ -883,6 +876,12 @@ __clear_subchannel_easy(struct subchannel_id schid) return -EBUSY; } +static void __clear_chsc_subchannel_easy(void) +{ + /* It seems we can only wait for a bit here :/ */ + udelay_reset(100); +} + static int pgm_check_occured; static void cio_reset_pgm_check_handler(void) @@ -921,11 +920,22 @@ static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data) case -ENODEV: break; default: /* -EBUSY */ - if (__clear_subchannel_easy(schid)) - break; /* give up... */ + switch (schib.pmcw.st) { + case SUBCHANNEL_TYPE_IO: + if (__clear_io_subchannel_easy(schid)) + goto out; /* give up... */ + break; + case SUBCHANNEL_TYPE_CHSC: + __clear_chsc_subchannel_easy(); + break; + default: + /* No default clear strategy */ + break; + } stsch(schid, &schib); __disable_subchannel_easy(schid, &schib); } +out: return 0; } @@ -1068,3 +1078,61 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) iplinfo->is_qdio = schib.pmcw.qf; return 0; } + +/** + * cio_tm_start_key - perform start function + * @sch: subchannel on which to perform the start function + * @tcw: transport-command word to be started + * @lpm: mask of paths to use + * @key: storage key to use for storage access + * + * Start the tcw on the given subchannel. Return zero on success, non-zero + * otherwise. + */ +int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key) +{ + int cc; + union orb *orb = &to_io_private(sch)->orb; + + memset(orb, 0, sizeof(union orb)); + orb->tm.intparm = (u32) (addr_t) sch; + orb->tm.key = key >> 4; + orb->tm.b = 1; + orb->tm.lpm = lpm ? lpm : sch->lpm; + orb->tm.tcw = (u32) (addr_t) tcw; + cc = ssch(sch->schid, orb); + switch (cc) { + case 0: + return 0; + case 1: + case 2: + return -EBUSY; + default: + return cio_start_handle_notoper(sch, lpm); + } +} + +/** + * cio_tm_intrg - perform interrogate function + * @sch - subchannel on which to perform the interrogate function + * + * If the specified subchannel is running in transport-mode, perform the + * interrogate function. Return zero on success, non-zero otherwie. + */ +int cio_tm_intrg(struct subchannel *sch) +{ + int cc; + + if (!to_io_private(sch)->orb.tm.b) + return -EINVAL; + cc = xsch(sch->schid); + switch (cc) { + case 0: + case 2: + return 0; + case 1: + return -EBUSY; + default: + return -ENODEV; + } +} diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 6e933aebe01..3b236d20e83 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -3,9 +3,12 @@ #include <linux/mutex.h> #include <linux/device.h> +#include <linux/mod_devicetable.h> #include <asm/chpid.h> +#include <asm/cio.h> +#include <asm/fcx.h> +#include <asm/schid.h> #include "chsc.h" -#include "schid.h" /* * path management control word @@ -13,7 +16,7 @@ struct pmcw { u32 intparm; /* interruption parameter */ u32 qf : 1; /* qdio facility */ - u32 res0 : 1; /* reserved zeros */ + u32 w : 1; u32 isc : 3; /* interruption sublass */ u32 res5 : 3; /* reserved zeros */ u32 ena : 1; /* enabled */ @@ -47,7 +50,7 @@ struct pmcw { */ struct schib { struct pmcw pmcw; /* path management control word */ - struct scsw scsw; /* subchannel status word */ + union scsw scsw; /* subchannel status word */ __u64 mba; /* measurement block address */ __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); @@ -99,8 +102,11 @@ extern int cio_set_options (struct subchannel *, int); extern int cio_get_options (struct subchannel *); extern int cio_modify (struct subchannel *); +int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key); +int cio_tm_intrg(struct subchannel *sch); + int cio_create_sch_lock(struct subchannel *); -void do_adapter_IO(void); +void do_adapter_IO(u8 isc); void do_IRQ(struct pt_regs *); /* Use with care. */ diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 2808b6833b9..a90b28c0be5 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -341,12 +341,12 @@ static int cmf_copy_block(struct ccw_device *cdev) if (stsch(sch->schid, &sch->schib)) return -ENODEV; - if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) { + if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) { /* Don't copy if a start function is in progress. */ - if ((!(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) && - (sch->schib.scsw.actl & + if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) && + (scsw_actl(&sch->schib.scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) && - (!(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS))) + (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS))) return -EBUSY; } cmb_data = cdev->private->cmb; @@ -612,9 +612,6 @@ static int alloc_cmb(struct ccw_device *cdev) free_pages((unsigned long)mem, get_order(size)); } else if (!mem) { /* no luck */ - printk(KERN_WARNING "cio: failed to allocate area " - "for measuring %d subchannels\n", - cmb_area.num_channels); ret = -ENOMEM; goto out; } else { @@ -1230,13 +1227,9 @@ static ssize_t cmb_enable_store(struct device *dev, switch (val) { case 0: ret = disable_cmf(cdev); - if (ret) - dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret); break; case 1: ret = enable_cmf(cdev); - if (ret && ret != -EBUSY) - dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret); break; } @@ -1344,8 +1337,7 @@ static int __init init_cmf(void) * to basic mode. */ if (format == CMF_AUTODETECT) { - if (!css_characteristics_avail || - !css_general_characteristics.ext_mb) { + if (!css_general_characteristics.ext_mb) { format = CMF_BASIC; } else { format = CMF_EXTENDED; @@ -1365,8 +1357,6 @@ static int __init init_cmf(void) cmbops = &cmbops_extended; break; default: - printk(KERN_ERR "cio: Invalid format %d for channel " - "measurement facility\n", format); return 1; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index a76956512b2..46c021d880d 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -2,8 +2,7 @@ * drivers/s390/cio/css.c * driver for channel subsystem * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation + * Copyright IBM Corp. 2002,2008 * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) */ @@ -14,7 +13,9 @@ #include <linux/errno.h> #include <linux/list.h> #include <linux/reboot.h> +#include <asm/isc.h> +#include "../s390mach.h" #include "css.h" #include "cio.h" #include "cio_debug.h" @@ -30,8 +31,6 @@ static int max_ssid = 0; struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; -int css_characteristics_avail = 0; - int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) { @@ -121,25 +120,6 @@ css_alloc_subchannel(struct subchannel_id schid) kfree(sch); return ERR_PTR(ret); } - - if (sch->st != SUBCHANNEL_TYPE_IO) { - /* For now we ignore all non-io subchannels. */ - kfree(sch); - return ERR_PTR(-EINVAL); - } - - /* - * Set intparm to subchannel address. - * This is fine even on 64bit since the subchannel is always located - * under 2G. - */ - sch->schib.pmcw.intparm = (u32)(addr_t)sch; - ret = cio_modify(sch); - if (ret) { - kfree(sch->lock); - kfree(sch); - return ERR_PTR(ret); - } return sch; } @@ -177,12 +157,18 @@ static int css_sch_device_register(struct subchannel *sch) return ret; } +/** + * css_sch_device_unregister - unregister a subchannel + * @sch: subchannel to be unregistered + */ void css_sch_device_unregister(struct subchannel *sch) { mutex_lock(&sch->reg_mutex); - device_unregister(&sch->dev); + if (device_is_registered(&sch->dev)) + device_unregister(&sch->dev); mutex_unlock(&sch->reg_mutex); } +EXPORT_SYMBOL_GPL(css_sch_device_unregister); static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) { @@ -229,6 +215,41 @@ void css_update_ssd_info(struct subchannel *sch) } } +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "%01x\n", sch->st); +} + +static DEVICE_ATTR(type, 0444, type_show, NULL); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "css:t%01X\n", sch->st); +} + +static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); + +static struct attribute *subch_attrs[] = { + &dev_attr_type.attr, + &dev_attr_modalias.attr, + NULL, +}; + +static struct attribute_group subch_attr_group = { + .attrs = subch_attrs, +}; + +static struct attribute_group *default_subch_attr_groups[] = { + &subch_attr_group, + NULL, +}; + static int css_register_subchannel(struct subchannel *sch) { int ret; @@ -237,16 +258,17 @@ static int css_register_subchannel(struct subchannel *sch) sch->dev.parent = &channel_subsystems[0]->device; sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; - sch->dev.groups = subch_attr_groups; + sch->dev.groups = default_subch_attr_groups; /* * We don't want to generate uevents for I/O subchannels that don't * have a working ccw device behind them since they will be * unregistered before they can be used anyway, so we delay the add * uevent until after device recognition was successful. + * Note that we suppress the uevent for all subchannel types; + * the subchannel driver can decide itself when it wants to inform + * userspace of its existence. */ - if (!cio_is_console(sch->schid)) - /* Console is special, no need to suppress. */ - sch->dev.uevent_suppress = 1; + sch->dev.uevent_suppress = 1; css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); @@ -255,10 +277,19 @@ static int css_register_subchannel(struct subchannel *sch) sch->schid.ssid, sch->schid.sch_no, ret); return ret; } + if (!sch->driver) { + /* + * No driver matched. Generate the uevent now so that + * a fitting driver module may be loaded based on the + * modalias. + */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); + } return ret; } -static int css_probe_device(struct subchannel_id schid) +int css_probe_device(struct subchannel_id schid) { int ret; struct subchannel *sch; @@ -301,116 +332,12 @@ int css_sch_is_valid(struct schib *schib) { if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) return 0; + if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w) + return 0; return 1; } EXPORT_SYMBOL_GPL(css_sch_is_valid); -static int css_get_subchannel_status(struct subchannel *sch) -{ - struct schib schib; - - if (stsch(sch->schid, &schib)) - return CIO_GONE; - if (!css_sch_is_valid(&schib)) - return CIO_GONE; - if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) - return CIO_REVALIDATE; - if (!sch->lpm) - return CIO_NO_PATH; - return CIO_OPER; -} - -static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) -{ - int event, ret, disc; - unsigned long flags; - enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; - - 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); - 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); - return -EAGAIN; - } - event = css_get_subchannel_status(sch); - CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", - sch->schid.ssid, sch->schid.sch_no, event, - disc ? "disconnected" : "normal", - slow ? "slow" : "fast"); - /* Analyze subchannel status. */ - action = NONE; - switch (event) { - case CIO_NO_PATH: - if (disc) { - /* Check if paths have become available. */ - action = REPROBE; - break; - } - /* fall through */ - case CIO_GONE: - /* Prevent unwanted effects when opening lock. */ - cio_disable_subchannel(sch); - device_set_disconnected(sch); - /* Ask driver what to do with device. */ - action = UNREGISTER; - if (sch->driver && sch->driver->notify) { - spin_unlock_irqrestore(sch->lock, flags); - ret = sch->driver->notify(sch, event); - spin_lock_irqsave(sch->lock, flags); - if (ret) - action = NONE; - } - break; - case CIO_REVALIDATE: - /* Device will be removed, so no notify necessary. */ - if (disc) - /* Reprobe because immediate unregister might block. */ - action = REPROBE; - else - action = UNREGISTER_PROBE; - break; - case CIO_OPER: - if (disc) - /* Get device operational again. */ - action = REPROBE; - break; - } - /* Perform action. */ - ret = 0; - switch (action) { - case UNREGISTER: - case UNREGISTER_PROBE: - /* Unregister device (will use subchannel lock). */ - spin_unlock_irqrestore(sch->lock, flags); - css_sch_device_unregister(sch); - spin_lock_irqsave(sch->lock, flags); - - /* Reset intparm to zeroes. */ - sch->schib.pmcw.intparm = 0; - cio_modify(sch); - break; - case REPROBE: - device_trigger_reprobe(sch); - break; - default: - break; - } - spin_unlock_irqrestore(sch->lock, flags); - /* Probe if necessary. */ - if (action == UNREGISTER_PROBE) - ret = css_probe_device(sch->schid); - - return ret; -} - static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) { struct schib schib; @@ -429,6 +356,21 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) return css_probe_device(schid); } +static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) +{ + int ret = 0; + + if (sch->driver) { + if (sch->driver->sch_event) + ret = sch->driver->sch_event(sch, slow); + else + dev_dbg(&sch->dev, + "Got subchannel machine check but " + "no sch_event handler provided.\n"); + } + return ret; +} + static void css_evaluate_subchannel(struct subchannel_id schid, int slow) { struct subchannel *sch; @@ -596,18 +538,29 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe); /* * Called from the machine check handler for subchannel report words. */ -void css_process_crw(int rsid1, int rsid2) +static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { struct subchannel_id mchk_schid; - CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n", - rsid1, rsid2); + if (overflow) { + css_schedule_eval_all(); + return; + } + CIO_CRW_EVENT(2, "CRW0 reports slct=%d, oflw=%d, " + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", + crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, + crw0->erc, crw0->rsid); + if (crw1) + CIO_CRW_EVENT(2, "CRW1 reports slct=%d, oflw=%d, " + "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", + crw1->slct, crw1->oflw, crw1->chn, crw1->rsc, + crw1->anc, crw1->erc, crw1->rsid); init_subchannel_id(&mchk_schid); - mchk_schid.sch_no = rsid1; - if (rsid2 != 0) - mchk_schid.ssid = (rsid2 >> 8) & 3; + mchk_schid.sch_no = crw0->rsid; + if (crw1) + mchk_schid.ssid = (crw1->rsid >> 8) & 3; - /* + /* * Since we are always presented with IPI in the CRW, we have to * use stsch() to find out if the subchannel in question has come * or gone. @@ -658,7 +611,7 @@ __init_channel_subsystem(struct subchannel_id schid, void *data) static void __init css_generate_pgid(struct channel_subsystem *css, u32 tod_high) { - if (css_characteristics_avail && css_general_characteristics.mcss) { + if (css_general_characteristics.mcss) { css->global_pgid.pgid_high.ext_cssid.version = 0x80; css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid; } else { @@ -795,8 +748,6 @@ init_channel_subsystem (void) ret = chsc_determine_css_characteristics(); if (ret == -ENOMEM) goto out; /* No need to continue. */ - if (ret == 0) - css_characteristics_avail = 1; ret = chsc_alloc_sei_area(); if (ret) @@ -806,6 +757,10 @@ init_channel_subsystem (void) if (ret) goto out; + ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw); + if (ret) + goto out; + if ((ret = bus_register(&css_bus_type))) goto out; @@ -836,8 +791,7 @@ init_channel_subsystem (void) ret = device_register(&css->device); if (ret) goto out_free_all; - if (css_characteristics_avail && - css_chsc_characteristics.secm) { + if (css_chsc_characteristics.secm) { ret = device_create_file(&css->device, &dev_attr_cm_enable); if (ret) @@ -852,7 +806,8 @@ init_channel_subsystem (void) goto out_pseudo; css_init_done = 1; - ctl_set_bit(6, 28); + /* Enable default isc for I/O subchannels. */ + isc_register(IO_SCH_ISC); for_each_subchannel(__init_channel_subsystem, NULL); return 0; @@ -875,7 +830,7 @@ out_unregister: i--; css = channel_subsystems[i]; device_unregister(&css->pseudo_subchannel->dev); - if (css_characteristics_avail && css_chsc_characteristics.secm) + if (css_chsc_characteristics.secm) device_remove_file(&css->device, &dev_attr_cm_enable); device_unregister(&css->device); @@ -883,6 +838,7 @@ out_unregister: out_bus: bus_unregister(&css_bus_type); out: + s390_unregister_crw_handler(CRW_RSC_CSS); chsc_free_sei_area(); kfree(slow_subchannel_set); printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n", @@ -895,19 +851,16 @@ 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 - * subchannel type although the device is an i/o subchannel - */ -static int -css_bus_match (struct device *dev, struct device_driver *drv) +static int css_bus_match(struct device *dev, struct device_driver *drv) { struct subchannel *sch = to_subchannel(dev); struct css_driver *driver = to_cssdriver(drv); + struct css_device_id *id; - if (sch->st == driver->subchannel_type) - return 1; + for (id = driver->subchannel_type; id->match_flags; id++) { + if (sch->st == id->type) + return 1; + } return 0; } @@ -945,12 +898,25 @@ static void css_shutdown(struct device *dev) sch->driver->shutdown(sch); } +static int css_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct subchannel *sch = to_subchannel(dev); + int ret; + + ret = add_uevent_var(env, "ST=%01X", sch->st); + if (ret) + return ret; + ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st); + return ret; +} + struct bus_type css_bus_type = { .name = "css", .match = css_bus_match, .probe = css_probe, .remove = css_remove, .shutdown = css_shutdown, + .uevent = css_uevent, }; /** @@ -985,4 +951,3 @@ subsys_initcall(init_channel_subsystem); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); -EXPORT_SYMBOL_GPL(css_characteristics_avail); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index e1913518f35..57ebf120f82 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -9,8 +9,7 @@ #include <asm/cio.h> #include <asm/chpid.h> - -#include "schid.h" +#include <asm/schid.h> /* * path grouping stuff @@ -58,20 +57,28 @@ struct pgid { __u32 tod_high; /* high word TOD clock */ } __attribute__ ((packed)); -/* - * A css driver handles all subchannels of one type. - * Currently, we only care about I/O subchannels (type 0), these - * have a ccw_device connected to them. - */ struct subchannel; +struct chp_link; +/** + * struct css_driver - device driver for subchannels + * @owner: owning module + * @subchannel_type: subchannel type supported by this driver + * @drv: embedded device driver structure + * @irq: called on interrupts + * @chp_event: called for events affecting a channel path + * @sch_event: called for events affecting the subchannel + * @probe: function called on probe + * @remove: function called on remove + * @shutdown: called at device shutdown + * @name: name of the device driver + */ struct css_driver { struct module *owner; - unsigned int subchannel_type; + struct css_device_id *subchannel_type; struct device_driver drv; void (*irq)(struct subchannel *); - int (*notify)(struct subchannel *, int); - void (*verify)(struct subchannel *); - void (*termination)(struct subchannel *); + int (*chp_event)(struct subchannel *, struct chp_link *, int); + int (*sch_event)(struct subchannel *, int); int (*probe)(struct subchannel *); int (*remove)(struct subchannel *); void (*shutdown)(struct subchannel *); @@ -89,13 +96,13 @@ extern int css_driver_register(struct css_driver *); extern void css_driver_unregister(struct css_driver *); extern void css_sch_device_unregister(struct subchannel *); -extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); +extern int css_probe_device(struct subchannel_id); +extern struct subchannel *get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), int (*fn_unknown)(struct subchannel_id, void *), void *data); extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); void css_update_ssd_info(struct subchannel *sch); @@ -121,20 +128,6 @@ struct channel_subsystem { extern struct bus_type css_bus_type; extern struct channel_subsystem *channel_subsystems[]; -/* Some helper functions for disconnected state. */ -int device_is_disconnected(struct subchannel *); -void device_set_disconnected(struct subchannel *); -void device_trigger_reprobe(struct subchannel *); - -/* Helper functions for vary on/off. */ -int device_is_online(struct subchannel *); -void device_kill_io(struct subchannel *); -void device_set_intretry(struct subchannel *sch); -int device_trigger_verify(struct subchannel *sch); - -/* Machine check helper function. */ -void device_kill_pending_timer(struct subchannel *); - /* Helper functions to build lists for the slow path. */ void css_schedule_eval(struct subchannel_id schid); void css_schedule_eval_all(void); @@ -145,6 +138,4 @@ int css_sch_is_valid(struct schib *); extern struct workqueue_struct *slow_path_wq; void css_wait_for_slow_path(void); - -extern struct attribute_group *subch_attr_groups[]; #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e22813db74a..e818d0c54c0 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -2,8 +2,7 @@ * drivers/s390/cio/device.c * bus driver for ccw devices * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation + * Copyright IBM Corp. 2002,2008 * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) @@ -23,7 +22,9 @@ #include <asm/cio.h> #include <asm/param.h> /* HZ */ #include <asm/cmb.h> +#include <asm/isc.h> +#include "chp.h" #include "cio.h" #include "cio_debug.h" #include "css.h" @@ -125,19 +126,24 @@ struct bus_type ccw_bus_type; static void io_subchannel_irq(struct subchannel *); static int io_subchannel_probe(struct subchannel *); static int io_subchannel_remove(struct subchannel *); -static int io_subchannel_notify(struct subchannel *, int); -static void io_subchannel_verify(struct subchannel *); -static void io_subchannel_ioterm(struct subchannel *); static void io_subchannel_shutdown(struct subchannel *); +static int io_subchannel_sch_event(struct subchannel *, int); +static int io_subchannel_chp_event(struct subchannel *, struct chp_link *, + int); + +static struct css_device_id io_subchannel_ids[] = { + { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_IO, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(css, io_subchannel_ids); static struct css_driver io_subchannel_driver = { .owner = THIS_MODULE, - .subchannel_type = SUBCHANNEL_TYPE_IO, + .subchannel_type = io_subchannel_ids, .name = "io_subchannel", .irq = io_subchannel_irq, - .notify = io_subchannel_notify, - .verify = io_subchannel_verify, - .termination = io_subchannel_ioterm, + .sch_event = io_subchannel_sch_event, + .chp_event = io_subchannel_chp_event, .probe = io_subchannel_probe, .remove = io_subchannel_remove, .shutdown = io_subchannel_shutdown, @@ -487,25 +493,22 @@ static int online_store_recog_and_online(struct ccw_device *cdev) ccw_device_set_online(cdev); return 0; } -static void online_store_handle_online(struct ccw_device *cdev, int force) +static int online_store_handle_online(struct ccw_device *cdev, int force) { int ret; ret = online_store_recog_and_online(cdev); if (ret) - return; + return ret; if (force && cdev->private->state == DEV_STATE_BOXED) { ret = ccw_device_stlck(cdev); - if (ret) { - dev_warn(&cdev->dev, - "ccw_device_stlck returned %d!\n", ret); - return; - } + if (ret) + return ret; if (cdev->id.cu_type == 0) cdev->private->state = DEV_STATE_NOT_OPER; online_store_recog_and_online(cdev); } - + return 0; } static ssize_t online_store (struct device *dev, struct device_attribute *attr, @@ -538,8 +541,9 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, ret = count; break; case 1: - online_store_handle_online(cdev, force); - ret = count; + ret = online_store_handle_online(cdev, force); + if (!ret) + ret = count; break; default: ret = -EINVAL; @@ -584,19 +588,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(availability, 0444, available_show, NULL); -static struct attribute * subch_attrs[] = { +static struct attribute *io_subchannel_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, NULL, }; -static struct attribute_group subch_attr_group = { - .attrs = subch_attrs, -}; - -struct attribute_group *subch_attr_groups[] = { - &subch_attr_group, - NULL, +static struct attribute_group io_subchannel_attr_group = { + .attrs = io_subchannel_attrs, }; static struct attribute * ccwdev_attrs[] = { @@ -790,7 +789,7 @@ static void sch_attach_device(struct subchannel *sch, sch_set_cdev(sch, cdev); cdev->private->schid = sch->schid; cdev->ccwlock = sch->lock; - device_trigger_reprobe(sch); + ccw_device_trigger_reprobe(cdev); spin_unlock_irq(sch->lock); } @@ -1037,7 +1036,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) struct ccw_device_private *priv; sch_set_cdev(sch, cdev); - sch->driver = &io_subchannel_driver; cdev->ccwlock = sch->lock; /* Init private data. */ @@ -1122,8 +1120,33 @@ static void io_subchannel_irq(struct subchannel *sch) dev_fsm_event(cdev, DEV_EVENT_INTERRUPT); } -static int -io_subchannel_probe (struct subchannel *sch) +static void io_subchannel_init_fields(struct subchannel *sch) +{ + if (cio_is_console(sch->schid)) + sch->opm = 0xff; + else + sch->opm = chp_get_sch_opm(sch); + sch->lpm = sch->schib.pmcw.pam & sch->opm; + sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC; + + CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X" + " - PIM = %02X, PAM = %02X, POM = %02X\n", + sch->schib.pmcw.dev, sch->schid.ssid, + sch->schid.sch_no, sch->schib.pmcw.pim, + sch->schib.pmcw.pam, sch->schib.pmcw.pom); + /* Initially set up some fields in the pmcw. */ + sch->schib.pmcw.ena = 0; + sch->schib.pmcw.csense = 1; /* concurrent sense */ + if ((sch->lpm & (sch->lpm - 1)) != 0) + sch->schib.pmcw.mp = 1; /* multipath mode */ + /* clean up possible residual cmf stuff */ + sch->schib.pmcw.mme = 0; + sch->schib.pmcw.mbfc = 0; + sch->schib.pmcw.mbi = 0; + sch->schib.mba = 0; +} + +static int io_subchannel_probe(struct subchannel *sch) { struct ccw_device *cdev; int rc; @@ -1132,11 +1155,21 @@ io_subchannel_probe (struct subchannel *sch) cdev = sch_get_cdev(sch); if (cdev) { + rc = sysfs_create_group(&sch->dev.kobj, + &io_subchannel_attr_group); + if (rc) + CIO_MSG_EVENT(0, "Failed to create io subchannel " + "attributes for subchannel " + "0.%x.%04x (rc=%d)\n", + sch->schid.ssid, sch->schid.sch_no, rc); /* * This subchannel already has an associated ccw_device. - * Register it and exit. This happens for all early - * device, e.g. the console. + * Throw the delayed uevent for the subchannel, register + * the ccw_device and exit. This happens for all early + * devices, e.g. the console. */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); cdev->dev.groups = ccwdev_attr_groups; device_initialize(&cdev->dev); ccw_device_register(cdev); @@ -1152,17 +1185,24 @@ io_subchannel_probe (struct subchannel *sch) get_device(&cdev->dev); return 0; } + io_subchannel_init_fields(sch); /* * 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; + rc = sysfs_create_group(&sch->dev.kobj, + &io_subchannel_attr_group); + if (rc) + return rc; /* Allocate I/O subchannel private data. */ sch->private = kzalloc(sizeof(struct io_subchannel_private), GFP_KERNEL | GFP_DMA); - if (!sch->private) - return -ENOMEM; + if (!sch->private) { + rc = -ENOMEM; + goto out_err; + } cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL); if (!cdev) cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent), @@ -1181,8 +1221,8 @@ io_subchannel_probe (struct subchannel *sch) } cdev = io_subchannel_create_ccwdev(sch); if (IS_ERR(cdev)) { - kfree(sch->private); - return PTR_ERR(cdev); + rc = PTR_ERR(cdev); + goto out_err; } rc = io_subchannel_recog(cdev, sch); if (rc) { @@ -1191,9 +1231,12 @@ io_subchannel_probe (struct subchannel *sch) spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); - kfree(sch->private); + goto out_err; } - + return 0; +out_err: + kfree(sch->private); + sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return rc; } @@ -1214,6 +1257,7 @@ io_subchannel_remove (struct subchannel *sch) ccw_device_unregister(cdev); put_device(&cdev->dev); kfree(sch->private); + sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group); return 0; } @@ -1224,11 +1268,7 @@ static int io_subchannel_notify(struct subchannel *sch, int event) cdev = sch_get_cdev(sch); if (!cdev) return 0; - if (!cdev->drv) - return 0; - if (!cdev->online) - return 0; - return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; + return ccw_device_notify(cdev, event); } static void io_subchannel_verify(struct subchannel *sch) @@ -1240,22 +1280,96 @@ static void io_subchannel_verify(struct subchannel *sch) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static void io_subchannel_ioterm(struct subchannel *sch) +static int check_for_io_on_path(struct subchannel *sch, int mask) { - struct ccw_device *cdev; + int cc; - cdev = sch_get_cdev(sch); - if (!cdev) - return; - /* Internal I/O will be retried by the interrupt handler. */ - if (cdev->private->flags.intretry) + cc = stsch(sch->schid, &sch->schib); + if (cc) + return 0; + if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask) + return 1; + return 0; +} + +static void terminate_internal_io(struct subchannel *sch, + struct ccw_device *cdev) +{ + if (cio_clear(sch)) { + /* Recheck device in case clear failed. */ + sch->lpm = 0; + if (cdev->online) + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + else + css_schedule_eval(sch->schid); return; + } cdev->private->state = DEV_STATE_CLEAR_VERIFY; + /* Request retry of internal operation. */ + cdev->private->flags.intretry = 1; + /* Call handler. */ if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); } +static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) +{ + struct ccw_device *cdev; + + cdev = sch_get_cdev(sch); + if (!cdev) + return; + if (check_for_io_on_path(sch, mask)) { + if (cdev->private->state == DEV_STATE_ONLINE) + ccw_device_kill_io(cdev); + else { + terminate_internal_io(sch, cdev); + /* Re-start path verification. */ + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + } + } else + /* trigger path verification. */ + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + +} + +static int io_subchannel_chp_event(struct subchannel *sch, + struct chp_link *link, int event) +{ + int mask; + + mask = chp_ssd_get_mask(&sch->ssd_info, link); + if (!mask) + return 0; + switch (event) { + case CHP_VARY_OFF: + sch->opm &= ~mask; + sch->lpm &= ~mask; + io_subchannel_terminate_path(sch, mask); + break; + case CHP_VARY_ON: + sch->opm |= mask; + sch->lpm |= mask; + io_subchannel_verify(sch); + break; + case CHP_OFFLINE: + if (stsch(sch->schid, &sch->schib)) + return -ENXIO; + if (!css_sch_is_valid(&sch->schib)) + return -ENODEV; + io_subchannel_terminate_path(sch, mask); + break; + case CHP_ONLINE: + if (stsch(sch->schid, &sch->schib)) + return -ENXIO; + sch->lpm |= mask & sch->opm; + io_subchannel_verify(sch); + break; + } + return 0; +} + static void io_subchannel_shutdown(struct subchannel *sch) { @@ -1285,6 +1399,195 @@ io_subchannel_shutdown(struct subchannel *sch) cio_disable_subchannel(sch); } +static int io_subchannel_get_status(struct subchannel *sch) +{ + struct schib schib; + + if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) + return CIO_GONE; + if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) + return CIO_REVALIDATE; + if (!sch->lpm) + return CIO_NO_PATH; + return CIO_OPER; +} + +static int device_is_disconnected(struct ccw_device *cdev) +{ + if (!cdev) + return 0; + return (cdev->private->state == DEV_STATE_DISCONNECTED || + cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); +} + +static int recovery_check(struct device *dev, void *data) +{ + struct ccw_device *cdev = to_ccwdev(dev); + int *redo = data; + + spin_lock_irq(cdev->ccwlock); + switch (cdev->private->state) { + case DEV_STATE_DISCONNECTED: + CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); + dev_fsm_event(cdev, DEV_EVENT_VERIFY); + *redo = 1; + break; + case DEV_STATE_DISCONNECTED_SENSE_ID: + *redo = 1; + break; + } + spin_unlock_irq(cdev->ccwlock); + + return 0; +} + +static void recovery_work_func(struct work_struct *unused) +{ + int redo = 0; + + bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); + if (redo) { + spin_lock_irq(&recovery_lock); + if (!timer_pending(&recovery_timer)) { + if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) + recovery_phase++; + mod_timer(&recovery_timer, jiffies + + recovery_delay[recovery_phase] * HZ); + } + spin_unlock_irq(&recovery_lock); + } else + CIO_MSG_EVENT(4, "recovery: end\n"); +} + +static DECLARE_WORK(recovery_work, recovery_work_func); + +static void recovery_func(unsigned long data) +{ + /* + * We can't do our recovery in softirq context and it's not + * performance critical, so we schedule it. + */ + schedule_work(&recovery_work); +} + +static void ccw_device_schedule_recovery(void) +{ + unsigned long flags; + + CIO_MSG_EVENT(4, "recovery: schedule\n"); + spin_lock_irqsave(&recovery_lock, flags); + if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { + recovery_phase = 0; + mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); + } + spin_unlock_irqrestore(&recovery_lock, flags); +} + +static void device_set_disconnected(struct ccw_device *cdev) +{ + if (!cdev) + return; + ccw_device_set_timeout(cdev, 0); + cdev->private->flags.fake_irb = 0; + cdev->private->state = DEV_STATE_DISCONNECTED; + if (cdev->online) + ccw_device_schedule_recovery(); +} + +static int io_subchannel_sch_event(struct subchannel *sch, int slow) +{ + int event, ret, disc; + unsigned long flags; + enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; + struct ccw_device *cdev; + + spin_lock_irqsave(sch->lock, flags); + cdev = sch_get_cdev(sch); + disc = device_is_disconnected(cdev); + if (disc && slow) { + /* Disconnected devices are evaluated directly only.*/ + spin_unlock_irqrestore(sch->lock, flags); + return 0; + } + /* No interrupt after machine check - kill pending timers. */ + if (cdev) + ccw_device_set_timeout(cdev, 0); + if (!disc && !slow) { + /* Non-disconnected devices are evaluated on the slow path. */ + spin_unlock_irqrestore(sch->lock, flags); + return -EAGAIN; + } + event = io_subchannel_get_status(sch); + CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", + sch->schid.ssid, sch->schid.sch_no, event, + disc ? "disconnected" : "normal", + slow ? "slow" : "fast"); + /* Analyze subchannel status. */ + action = NONE; + switch (event) { + case CIO_NO_PATH: + if (disc) { + /* Check if paths have become available. */ + action = REPROBE; + break; + } + /* fall through */ + case CIO_GONE: + /* Prevent unwanted effects when opening lock. */ + cio_disable_subchannel(sch); + device_set_disconnected(cdev); + /* Ask driver what to do with device. */ + action = UNREGISTER; + spin_unlock_irqrestore(sch->lock, flags); + ret = io_subchannel_notify(sch, event); + spin_lock_irqsave(sch->lock, flags); + if (ret) + action = NONE; + break; + case CIO_REVALIDATE: + /* Device will be removed, so no notify necessary. */ + if (disc) + /* Reprobe because immediate unregister might block. */ + action = REPROBE; + else + action = UNREGISTER_PROBE; + break; + case CIO_OPER: + if (disc) + /* Get device operational again. */ + action = REPROBE; + break; + } + /* Perform action. */ + ret = 0; + switch (action) { + case UNREGISTER: + case UNREGISTER_PROBE: + /* Unregister device (will use subchannel lock). */ + spin_unlock_irqrestore(sch->lock, flags); + css_sch_device_unregister(sch); + spin_lock_irqsave(sch->lock, flags); + + /* Reset intparm to zeroes. */ + sch->schib.pmcw.intparm = 0; + cio_modify(sch); + break; + case REPROBE: + ccw_device_trigger_reprobe(cdev); + break; + default: + break; + } + spin_unlock_irqrestore(sch->lock, flags); + /* Probe if necessary. */ + if (action == UNREGISTER_PROBE) + ret = css_probe_device(sch->schid); + + return ret; +} + #ifdef CONFIG_CCW_CONSOLE static struct ccw_device console_cdev; static struct ccw_device_private console_private; @@ -1297,14 +1600,16 @@ spinlock_t * cio_get_console_lock(void) return &ccw_console_lock; } -static int -ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch) +static int ccw_device_console_enable(struct ccw_device *cdev, + struct subchannel *sch) { int rc; /* Attach subchannel private data. */ sch->private = cio_get_console_priv(); memset(sch->private, 0, sizeof(struct io_subchannel_private)); + io_subchannel_init_fields(sch); + sch->driver = &io_subchannel_driver; /* Initialize the ccw_device structure. */ cdev->dev.parent= &sch->dev; rc = io_subchannel_recog(cdev, sch); @@ -1515,71 +1820,6 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) return sch->schid; } -static int recovery_check(struct device *dev, void *data) -{ - struct ccw_device *cdev = to_ccwdev(dev); - int *redo = data; - - spin_lock_irq(cdev->ccwlock); - switch (cdev->private->state) { - case DEV_STATE_DISCONNECTED: - CIO_MSG_EVENT(4, "recovery: trigger 0.%x.%04x\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno); - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - *redo = 1; - break; - case DEV_STATE_DISCONNECTED_SENSE_ID: - *redo = 1; - break; - } - spin_unlock_irq(cdev->ccwlock); - - return 0; -} - -static void recovery_work_func(struct work_struct *unused) -{ - int redo = 0; - - bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); - if (redo) { - spin_lock_irq(&recovery_lock); - if (!timer_pending(&recovery_timer)) { - if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) - recovery_phase++; - mod_timer(&recovery_timer, jiffies + - recovery_delay[recovery_phase] * HZ); - } - spin_unlock_irq(&recovery_lock); - } else - CIO_MSG_EVENT(4, "recovery: end\n"); -} - -static DECLARE_WORK(recovery_work, recovery_work_func); - -static void recovery_func(unsigned long data) -{ - /* - * We can't do our recovery in softirq context and it's not - * performance critical, so we schedule it. - */ - schedule_work(&recovery_work); -} - -void ccw_device_schedule_recovery(void) -{ - unsigned long flags; - - CIO_MSG_EVENT(4, "recovery: schedule\n"); - spin_lock_irqsave(&recovery_lock, flags); - if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { - recovery_phase = 0; - mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); - } - spin_unlock_irqrestore(&recovery_lock, flags); -} - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index cb08092be39..9800a8335a3 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -88,8 +88,6 @@ int ccw_device_recognition(struct ccw_device *); int ccw_device_online(struct ccw_device *); int ccw_device_offline(struct ccw_device *); -void ccw_device_schedule_recovery(void); - /* Function prototypes for device status and basic sense stuff. */ void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); @@ -118,6 +116,11 @@ int ccw_device_call_handler(struct ccw_device *); int ccw_device_stlck(struct ccw_device *); +/* Helper function for machine check handling. */ +void ccw_device_trigger_reprobe(struct ccw_device *); +void ccw_device_kill_io(struct ccw_device *); +int ccw_device_notify(struct ccw_device *, int); + /* qdio needs this. */ void ccw_device_set_timeout(struct ccw_device *, int); extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e268d5a77c1..8b5fe57fb2f 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -2,8 +2,7 @@ * drivers/s390/cio/device_fsm.c * finite state machine for device handling * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation + * Copyright IBM Corp. 2002,2008 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) */ @@ -27,65 +26,6 @@ static int timeout_log_enabled; -int -device_is_online(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return 0; - return (cdev->private->state == DEV_STATE_ONLINE); -} - -int -device_is_disconnected(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return 0; - return (cdev->private->state == DEV_STATE_DISCONNECTED || - cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); -} - -void -device_set_disconnected(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return; - ccw_device_set_timeout(cdev, 0); - cdev->private->flags.fake_irb = 0; - cdev->private->state = DEV_STATE_DISCONNECTED; - if (cdev->online) - ccw_device_schedule_recovery(); -} - -void device_set_intretry(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return; - cdev->private->flags.intretry = 1; -} - -int device_trigger_verify(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev || !cdev->online) - return -EINVAL; - dev_fsm_event(cdev, DEV_EVENT_VERIFY); - return 0; -} - static int __init ccw_timeout_log_setup(char *unused) { timeout_log_enabled = 1; @@ -99,31 +39,43 @@ static void ccw_timeout_log(struct ccw_device *cdev) struct schib schib; struct subchannel *sch; struct io_subchannel_private *private; + union orb *orb; int cc; sch = to_subchannel(cdev->dev.parent); private = to_io_private(sch); + orb = &private->orb; cc = stsch(sch->schid, &schib); printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " "device information:\n", get_clock()); printk(KERN_WARNING "cio: orb:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, - &private->orb, sizeof(private->orb), 0); + orb, sizeof(*orb), 0); printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id); printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id); printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, " "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm); - if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw || - (void *)(addr_t)private->orb.cpa == cdev->private->iccws) - printk(KERN_WARNING "cio: last channel program (intern):\n"); - else - printk(KERN_WARNING "cio: last channel program:\n"); - - print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, - (void *)(addr_t)private->orb.cpa, - sizeof(struct ccw1), 0); + if (orb->tm.b) { + printk(KERN_WARNING "cio: orb indicates transport mode\n"); + printk(KERN_WARNING "cio: last tcw:\n"); + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + (void *)(addr_t)orb->tm.tcw, + sizeof(struct tcw), 0); + } else { + printk(KERN_WARNING "cio: orb indicates command mode\n"); + if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw || + (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws) + printk(KERN_WARNING "cio: last channel program " + "(intern):\n"); + else + printk(KERN_WARNING "cio: last channel program:\n"); + + print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, + (void *)(addr_t)orb->cmd.cpa, + sizeof(struct ccw1), 0); + } printk(KERN_WARNING "cio: ccw device state: %d\n", cdev->private->state); printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc); @@ -171,18 +123,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires) add_timer(&cdev->private->timer); } -/* Kill any pending timers after machine check. */ -void -device_kill_pending_timer(struct subchannel *sch) -{ - struct ccw_device *cdev; - - cdev = sch_get_cdev(sch); - if (!cdev) - return; - ccw_device_set_timeout(cdev, 0); -} - /* * Cancel running i/o. This is called repeatedly since halt/clear are * asynchronous operations. We do one try with cio_cancel, two tries @@ -205,15 +145,18 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) /* Not operational -> done. */ return 0; /* Stage 1: cancel io. */ - if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) && - !(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) { - ret = cio_cancel(sch); - if (ret != -EINVAL) - return ret; - /* cancel io unsuccessful. From now on it is asynchronous. */ + if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) && + !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { + if (!scsw_is_tm(&sch->schib.scsw)) { + ret = cio_cancel(sch); + if (ret != -EINVAL) + return ret; + } + /* cancel io unsuccessful or not applicable (transport mode). + * Continue with asynchronous instructions. */ cdev->private->iretry = 3; /* 3 halt retries. */ } - if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) { + if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) { /* Stage 2: halt io. */ if (cdev->private->iretry) { cdev->private->iretry--; @@ -388,34 +331,30 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err) } } +int ccw_device_notify(struct ccw_device *cdev, int event) +{ + if (!cdev->drv) + return 0; + if (!cdev->online) + return 0; + return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; +} + static void ccw_device_oper_notify(struct work_struct *work) { struct ccw_device_private *priv; struct ccw_device *cdev; - struct subchannel *sch; int ret; - unsigned long flags; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; - spin_lock_irqsave(cdev->ccwlock, flags); - sch = to_subchannel(cdev->dev.parent); - if (sch->driver && sch->driver->notify) { - spin_unlock_irqrestore(cdev->ccwlock, flags); - ret = sch->driver->notify(sch, CIO_OPER); - spin_lock_irqsave(cdev->ccwlock, flags); - } else - ret = 0; + ret = ccw_device_notify(cdev, CIO_OPER); if (ret) { /* Reenable channel measurements, if needed. */ - spin_unlock_irqrestore(cdev->ccwlock, flags); cmf_reenable(cdev); - spin_lock_irqsave(cdev->ccwlock, flags); wake_up(&cdev->private->wait_q); - } - spin_unlock_irqrestore(cdev->ccwlock, flags); - if (!ret) + } else /* Driver doesn't want device back. */ ccw_device_do_unreg_rereg(work); } @@ -621,10 +560,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) /* Deliver fake irb to device driver, if needed. */ if (cdev->private->flags.fake_irb) { memset(&cdev->private->irb, 0, sizeof(struct irb)); - cdev->private->irb.scsw.cc = 1; - cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC; - cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND; - cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND; + cdev->private->irb.scsw.cmd.cc = 1; + cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC; + cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND; + cdev->private->irb.scsw.cmd.stctl = + SCSW_STCTL_STATUS_PEND; cdev->private->flags.fake_irb = 0; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, @@ -718,13 +658,10 @@ ccw_device_offline(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv) return -ENODEV; - if (cdev->private->state != DEV_STATE_ONLINE) { - if (sch->schib.scsw.actl != 0) - return -EBUSY; - return -EINVAL; - } - if (sch->schib.scsw.actl != 0) + if (scsw_actl(&sch->schib.scsw) != 0) return -EBUSY; + if (cdev->private->state != DEV_STATE_ONLINE) + return -EINVAL; /* Are we doing path grouping? */ if (!cdev->private->options.pgroup) { /* No, set state offline immediately. */ @@ -799,9 +736,9 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event) */ stsch(sch->schid, &sch->schib); - if (sch->schib.scsw.actl != 0 || - (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) || - (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) { + if (scsw_actl(&sch->schib.scsw) != 0 || + (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) || + (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) { /* * No final status yet or final status not yet delivered * to the device driver. Can't do path verfication now, @@ -823,13 +760,13 @@ static void ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event) { struct irb *irb; + int is_cmd; irb = (struct irb *) __LC_IRB; + is_cmd = !scsw_is_tm(&irb->scsw); /* Check for unsolicited interrupt. */ - if ((irb->scsw.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) - && (!irb->scsw.cc)) { - if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && + if (!scsw_is_solicited(&irb->scsw)) { + if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && !irb->esw.esw0.erw.cons) { /* Unit check but no sense data. Need basic sense. */ if (ccw_device_do_sense(cdev, irb) != 0) @@ -848,7 +785,7 @@ call_handler_unsol: } /* Accumulate status and find out if a basic sense is needed. */ ccw_device_accumulate_irb(cdev, irb); - if (cdev->private->flags.dosense) { + if (is_cmd && cdev->private->flags.dosense) { if (ccw_device_do_sense(cdev, irb) == 0) { cdev->private->state = DEV_STATE_W4SENSE; } @@ -892,9 +829,9 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; /* Check for unsolicited interrupt. */ - if (irb->scsw.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (irb->scsw.cc == 1) + if (scsw_stctl(&irb->scsw) == + (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { + if (scsw_cc(&irb->scsw) == 1) /* Basic sense hasn't started. Try again. */ ccw_device_do_sense(cdev, irb); else { @@ -912,7 +849,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) * only deliver the halt/clear interrupt to the device driver as if it * had killed the original request. */ - if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { + if (scsw_fctl(&irb->scsw) & + (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) { /* Retry Basic Sense if requested. */ if (cdev->private->flags.intretry) { cdev->private->flags.intretry = 0; @@ -986,12 +924,10 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) ERR_PTR(-EIO)); } -void device_kill_io(struct subchannel *sch) +void ccw_device_kill_io(struct ccw_device *cdev) { int ret; - struct ccw_device *cdev; - cdev = sch_get_cdev(sch); ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); @@ -1021,9 +957,9 @@ ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event) case DEV_EVENT_INTERRUPT: irb = (struct irb *) __LC_IRB; /* Check for unsolicited interrupt. */ - if ((irb->scsw.stctl == + if ((scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && - (!irb->scsw.cc)) + (!scsw_cc(&irb->scsw))) /* FIXME: we should restart stlck here, but this * is extremely unlikely ... */ goto out_wakeup; @@ -1055,17 +991,14 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_sense_id_start(cdev); } -void -device_trigger_reprobe(struct subchannel *sch) +void ccw_device_trigger_reprobe(struct ccw_device *cdev) { - struct ccw_device *cdev; + struct subchannel *sch; - cdev = sch_get_cdev(sch); - if (!cdev) - return; if (cdev->private->state != DEV_STATE_DISCONNECTED) return; + sch = to_subchannel(cdev->dev.parent); /* Update some values. */ if (stsch(sch->schid, &sch->schib)) return; @@ -1081,7 +1014,6 @@ device_trigger_reprobe(struct subchannel *sch) sch->schib.pmcw.ena = 0; if ((sch->lpm & (sch->lpm - 1)) != 0) sch->schib.pmcw.mp = 1; - sch->schib.pmcw.intparm = (u32)(addr_t)sch; /* We should also udate ssd info, but this has to wait. */ /* Check if this is another device which appeared on the same sch. */ if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index cba7020517e..1bdaa614e34 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -196,7 +196,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev) irb = &cdev->private->irb; /* Check the error cases. */ - if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { /* Retry Sense ID if requested. */ if (cdev->private->flags.intretry) { cdev->private->flags.intretry = 0; @@ -234,10 +234,10 @@ ccw_device_check_sense_id(struct ccw_device *cdev) irb->ecw[6], irb->ecw[7]); return -EAGAIN; } - if (irb->scsw.cc == 3) { + if (irb->scsw.cmd.cc == 3) { u8 lpm; - lpm = to_io_private(sch)->orb.lpm; + lpm = to_io_private(sch)->orb.cmd.lpm; if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x " "on subchannel 0.%x.%04x is " @@ -248,9 +248,9 @@ ccw_device_check_sense_id(struct ccw_device *cdev) } /* Did we get a proper answer ? */ - if (irb->scsw.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF && + if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF && cdev->private->senseid.reserved == 0xFF) { - if (irb->scsw.count < sizeof(struct senseid) - 8) + if (irb->scsw.cmd.count < sizeof(struct senseid) - 8) cdev->private->flags.esid = 1; return 0; /* Success */ } @@ -260,7 +260,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev) "subchannel 0.%x.%04x returns status %02X%02X\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no, - irb->scsw.dstat, irb->scsw.cstat); + irb->scsw.cmd.dstat, irb->scsw.cmd.cstat); return -EAGAIN; } @@ -277,9 +277,9 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); irb = (struct irb *) __LC_IRB; /* Retry sense id, if needed. */ - if (irb->scsw.stctl == + if (irb->scsw.cmd.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if ((irb->scsw.cc == 1) || !irb->scsw.actl) { + if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) { ret = __ccw_device_sense_id_start(cdev); if (ret && ret != -EBUSY) ccw_device_sense_id_done(cdev, ret); diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index f308ad55a6d..ee1a28310fb 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -17,6 +17,7 @@ #include <asm/ccwdev.h> #include <asm/idals.h> #include <asm/chpid.h> +#include <asm/fcx.h> #include "cio.h" #include "cio_debug.h" @@ -179,8 +180,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, return -EBUSY; } if (cdev->private->state != DEV_STATE_ONLINE || - ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) && - !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) || + ((sch->schib.scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) && + !(sch->schib.scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS)) || cdev->private->flags.doverify) return -EBUSY; ret = cio_set_options (sch, flags); @@ -379,7 +380,7 @@ int ccw_device_resume(struct ccw_device *cdev) if (cdev->private->state == DEV_STATE_NOT_OPER) return -ENODEV; if (cdev->private->state != DEV_STATE_ONLINE || - !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) + !(sch->schib.scsw.cmd.actl & SCSW_ACTL_SUSPENDED)) return -EINVAL; return cio_resume(sch); } @@ -404,7 +405,7 @@ ccw_device_call_handler(struct ccw_device *cdev) * - fast notification was requested (primary status) * - unsolicited interrupts */ - stctl = cdev->private->irb.scsw.stctl; + stctl = scsw_stctl(&cdev->private->irb.scsw); ending_status = (stctl & SCSW_STCTL_SEC_STATUS) || (stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) || (stctl == SCSW_STCTL_STATUS_PEND); @@ -528,14 +529,15 @@ ccw_device_stlck(struct ccw_device *cdev) cio_disable_subchannel(sch); //FIXME: return code? goto out_unlock; } - cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND; + cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; spin_unlock_irqrestore(sch->lock, flags); - wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0); + wait_event(cdev->private->wait_q, + cdev->private->irb.scsw.cmd.actl == 0); spin_lock_irqsave(sch->lock, flags); cio_disable_subchannel(sch); //FIXME: return code? - if ((cdev->private->irb.scsw.dstat != + if ((cdev->private->irb.scsw.cmd.dstat != (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || - (cdev->private->irb.scsw.cstat != 0)) + (cdev->private->irb.scsw.cmd.cstat != 0)) ret = -EIO; /* Clear irb. */ memset(&cdev->private->irb, 0, sizeof(struct irb)); @@ -568,6 +570,122 @@ void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id) } EXPORT_SYMBOL(ccw_device_get_id); +/** + * ccw_device_tm_start_key - perform start function + * @cdev: ccw device on which to perform the start function + * @tcw: transport-command word to be started + * @intparm: user defined parameter to be passed to the interrupt handler + * @lpm: mask of paths to use + * @key: storage key to use for storage access + * + * Start the tcw on the given ccw device. Return zero on success, non-zero + * otherwise. + */ +int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, + unsigned long intparm, u8 lpm, u8 key) +{ + struct subchannel *sch; + int rc; + + sch = to_subchannel(cdev->dev.parent); + if (cdev->private->state != DEV_STATE_ONLINE) + return -EIO; + /* Adjust requested path mask to excluded varied off paths. */ + if (lpm) { + lpm &= sch->opm; + if (lpm == 0) + return -EACCES; + } + rc = cio_tm_start_key(sch, tcw, lpm, key); + if (rc == 0) + cdev->private->intparm = intparm; + return rc; +} +EXPORT_SYMBOL(ccw_device_tm_start_key); + +/** + * ccw_device_tm_start_timeout_key - perform start function + * @cdev: ccw device on which to perform the start function + * @tcw: transport-command word to be started + * @intparm: user defined parameter to be passed to the interrupt handler + * @lpm: mask of paths to use + * @key: storage key to use for storage access + * @expires: time span in jiffies after which to abort request + * + * Start the tcw on the given ccw device. Return zero on success, non-zero + * otherwise. + */ +int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw, + unsigned long intparm, u8 lpm, u8 key, + int expires) +{ + int ret; + + ccw_device_set_timeout(cdev, expires); + ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key); + if (ret != 0) + ccw_device_set_timeout(cdev, 0); + return ret; +} +EXPORT_SYMBOL(ccw_device_tm_start_timeout_key); + +/** + * ccw_device_tm_start - perform start function + * @cdev: ccw device on which to perform the start function + * @tcw: transport-command word to be started + * @intparm: user defined parameter to be passed to the interrupt handler + * @lpm: mask of paths to use + * + * Start the tcw on the given ccw device. Return zero on success, non-zero + * otherwise. + */ +int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw, + unsigned long intparm, u8 lpm) +{ + return ccw_device_tm_start_key(cdev, tcw, intparm, lpm, + PAGE_DEFAULT_KEY); +} +EXPORT_SYMBOL(ccw_device_tm_start); + +/** + * ccw_device_tm_start_timeout - perform start function + * @cdev: ccw device on which to perform the start function + * @tcw: transport-command word to be started + * @intparm: user defined parameter to be passed to the interrupt handler + * @lpm: mask of paths to use + * @expires: time span in jiffies after which to abort request + * + * Start the tcw on the given ccw device. Return zero on success, non-zero + * otherwise. + */ +int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw, + unsigned long intparm, u8 lpm, int expires) +{ + return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm, + PAGE_DEFAULT_KEY, expires); +} +EXPORT_SYMBOL(ccw_device_tm_start_timeout); + +/** + * ccw_device_tm_intrg - perform interrogate function + * @cdev: ccw device on which to perform the interrogate function + * + * Perform an interrogate function on the given ccw device. Return zero on + * success, non-zero otherwise. + */ +int ccw_device_tm_intrg(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + if (cdev->private->state != DEV_STATE_ONLINE) + return -EIO; + if (!scsw_is_tm(&sch->schib.scsw) || + !(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND)) + return -EINVAL; + return cio_tm_intrg(sch); +} +EXPORT_SYMBOL(ccw_device_tm_intrg); + // FIXME: these have to go: int diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 5cf7be008e9..86bc94eb607 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -28,13 +28,13 @@ * Helper function called from interrupt context to decide whether an * operation should be tried again. */ -static int __ccw_device_should_retry(struct scsw *scsw) +static int __ccw_device_should_retry(union scsw *scsw) { /* CC is only valid if start function bit is set. */ - if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1) + if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) return 1; /* No more activity. For sense and set PGID we stubbornly try again. */ - if (!scsw->actl) + if (!scsw->cmd.actl) return 1; return 0; } @@ -125,7 +125,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); irb = &cdev->private->irb; - if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { /* Retry Sense PGID if requested. */ if (cdev->private->flags.intretry) { cdev->private->flags.intretry = 0; @@ -155,10 +155,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) irb->ecw[6], irb->ecw[7]); return -EAGAIN; } - if (irb->scsw.cc == 3) { + if (irb->scsw.cmd.cc == 3) { u8 lpm; - lpm = to_io_private(sch)->orb.lpm; + lpm = to_io_private(sch)->orb.cmd.lpm; CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," " lpm %02X, became 'not operational'\n", cdev->private->dev_id.devno, sch->schid.ssid, @@ -188,7 +188,7 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; - if (irb->scsw.stctl == + if (irb->scsw.cmd.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { if (__ccw_device_should_retry(&irb->scsw)) { ret = __ccw_device_sense_pgid_start(cdev); @@ -331,7 +331,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); irb = &cdev->private->irb; - if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { /* Retry Set PGID if requested. */ if (cdev->private->flags.intretry) { cdev->private->flags.intretry = 0; @@ -355,7 +355,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev) irb->ecw[6], irb->ecw[7]); return -EAGAIN; } - if (irb->scsw.cc == 3) { + if (irb->scsw.cmd.cc == 3) { CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," " lpm %02X, became 'not operational'\n", cdev->private->dev_id.devno, sch->schid.ssid, @@ -376,7 +376,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev) sch = to_subchannel(cdev->dev.parent); irb = &cdev->private->irb; - if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { /* Retry NOP if requested. */ if (cdev->private->flags.intretry) { cdev->private->flags.intretry = 0; @@ -384,7 +384,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev) } return -ETIME; } - if (irb->scsw.cc == 3) { + if (irb->scsw.cmd.cc == 3) { CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," " lpm %02X, became 'not operational'\n", cdev->private->dev_id.devno, sch->schid.ssid, @@ -438,7 +438,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; - if (irb->scsw.stctl == + if (irb->scsw.cmd.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { if (__ccw_device_should_retry(&irb->scsw)) __ccw_device_verify_start(cdev); @@ -544,7 +544,7 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) irb = (struct irb *) __LC_IRB; - if (irb->scsw.stctl == + if (irb->scsw.cmd.stctl == (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { if (__ccw_device_should_retry(&irb->scsw)) __ccw_device_disband_start(cdev); diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 4a38993000f..1b03c5423be 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -29,9 +29,11 @@ static void ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb) { - if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK | - SCHN_STAT_CHN_CTRL_CHK | - SCHN_STAT_INTF_CTRL_CHK))) + char dbf_text[15]; + + if (!scsw_is_valid_cstat(&irb->scsw) || + !(scsw_cstat(&irb->scsw) & (SCHN_STAT_CHN_DATA_CHK | + SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK))) return; CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check " "received" @@ -39,15 +41,10 @@ ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb) ": %02X sch_stat : %02X\n", cdev->private->dev_id.devno, cdev->private->schid.ssid, cdev->private->schid.sch_no, - irb->scsw.dstat, irb->scsw.cstat); - - if (irb->scsw.cc != 3) { - char dbf_text[15]; - - sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no); - CIO_TRACE_EVENT(0, dbf_text); - CIO_HEX_EVENT(0, irb, sizeof (struct irb)); - } + scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw)); + sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no); + CIO_TRACE_EVENT(0, dbf_text); + CIO_HEX_EVENT(0, irb, sizeof(struct irb)); } /* @@ -81,12 +78,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb) * are condition that have to be met for the extended control * bit to have meaning. Sick. */ - cdev->private->irb.scsw.ectl = 0; - if ((irb->scsw.stctl & SCSW_STCTL_ALERT_STATUS) && - !(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS)) - cdev->private->irb.scsw.ectl = irb->scsw.ectl; + cdev->private->irb.scsw.cmd.ectl = 0; + if ((irb->scsw.cmd.stctl & SCSW_STCTL_ALERT_STATUS) && + !(irb->scsw.cmd.stctl & SCSW_STCTL_INTER_STATUS)) + cdev->private->irb.scsw.cmd.ectl = irb->scsw.cmd.ectl; /* Check if extended control word is valid. */ - if (!cdev->private->irb.scsw.ectl) + if (!cdev->private->irb.scsw.cmd.ectl) return; /* Copy concurrent sense / model dependent information. */ memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw)); @@ -98,11 +95,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb) static int ccw_device_accumulate_esw_valid(struct irb *irb) { - if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) + if (!irb->scsw.cmd.eswf && + (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND)) return 0; - if (irb->scsw.stctl == - (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) && - !(irb->scsw.actl & SCSW_ACTL_SUSPENDED)) + if (irb->scsw.cmd.stctl == + (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) && + !(irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)) return 0; return 1; } @@ -125,7 +123,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) cdev_irb->esw.esw1.lpum = irb->esw.esw1.lpum; /* Copy subchannel logout information if esw is of format 0. */ - if (irb->scsw.eswf) { + if (irb->scsw.cmd.eswf) { cdev_sublog = &cdev_irb->esw.esw0.sublog; sublog = &irb->esw.esw0.sublog; /* Copy extended status flags. */ @@ -134,7 +132,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) * Copy fields that have a meaning for channel data check * channel control check and interface control check. */ - if (irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK | + if (irb->scsw.cmd.cstat & (SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK)) { /* Copy ancillary report bit. */ @@ -155,7 +153,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb) /* Copy i/o-error alert. */ cdev_sublog->ioerr = sublog->ioerr; /* Copy channel path timeout bit. */ - if (irb->scsw.cstat & SCHN_STAT_INTF_CTRL_CHK) + if (irb->scsw.cmd.cstat & SCHN_STAT_INTF_CTRL_CHK) cdev_irb->esw.esw0.erw.cpt = irb->esw.esw0.erw.cpt; /* Copy failing storage address validity flag. */ cdev_irb->esw.esw0.erw.fsavf = irb->esw.esw0.erw.fsavf; @@ -200,24 +198,24 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) * If not, the remaining bit have no meaning and we must ignore them. * The esw is not meaningful as well... */ - if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) + if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) return; /* Check for channel checks and interface control checks. */ ccw_device_msg_control_check(cdev, irb); /* Check for path not operational. */ - if (irb->scsw.pno && irb->scsw.fctl != 0 && - (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) || - (irb->scsw.actl & SCSW_ACTL_SUSPENDED))) + if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw)) ccw_device_path_notoper(cdev); - + /* No irb accumulation for transport mode irbs. */ + if (scsw_is_tm(&irb->scsw)) { + memcpy(&cdev->private->irb, irb, sizeof(struct irb)); + return; + } /* * Don't accumulate unsolicited interrupts. */ - if ((irb->scsw.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) && - (!irb->scsw.cc)) + if (!scsw_is_solicited(&irb->scsw)) return; cdev_irb = &cdev->private->irb; @@ -227,62 +225,63 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) * status at the subchannel has been cleared and we must not pass * intermediate accumulated status to the device driver. */ - if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) + if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) memset(&cdev->private->irb, 0, sizeof(struct irb)); /* Copy bits which are valid only for the start function. */ - if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) { + if (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) { /* Copy key. */ - cdev_irb->scsw.key = irb->scsw.key; + cdev_irb->scsw.cmd.key = irb->scsw.cmd.key; /* Copy suspend control bit. */ - cdev_irb->scsw.sctl = irb->scsw.sctl; + cdev_irb->scsw.cmd.sctl = irb->scsw.cmd.sctl; /* Accumulate deferred condition code. */ - cdev_irb->scsw.cc |= irb->scsw.cc; + cdev_irb->scsw.cmd.cc |= irb->scsw.cmd.cc; /* Copy ccw format bit. */ - cdev_irb->scsw.fmt = irb->scsw.fmt; + cdev_irb->scsw.cmd.fmt = irb->scsw.cmd.fmt; /* Copy prefetch bit. */ - cdev_irb->scsw.pfch = irb->scsw.pfch; + cdev_irb->scsw.cmd.pfch = irb->scsw.cmd.pfch; /* Copy initial-status-interruption-control. */ - cdev_irb->scsw.isic = irb->scsw.isic; + cdev_irb->scsw.cmd.isic = irb->scsw.cmd.isic; /* Copy address limit checking control. */ - cdev_irb->scsw.alcc = irb->scsw.alcc; + cdev_irb->scsw.cmd.alcc = irb->scsw.cmd.alcc; /* Copy suppress suspend bit. */ - cdev_irb->scsw.ssi = irb->scsw.ssi; + cdev_irb->scsw.cmd.ssi = irb->scsw.cmd.ssi; } /* Take care of the extended control bit and extended control word. */ ccw_device_accumulate_ecw(cdev, irb); /* Accumulate function control. */ - cdev_irb->scsw.fctl |= irb->scsw.fctl; + cdev_irb->scsw.cmd.fctl |= irb->scsw.cmd.fctl; /* Copy activity control. */ - cdev_irb->scsw.actl= irb->scsw.actl; + cdev_irb->scsw.cmd.actl = irb->scsw.cmd.actl; /* Accumulate status control. */ - cdev_irb->scsw.stctl |= irb->scsw.stctl; + cdev_irb->scsw.cmd.stctl |= irb->scsw.cmd.stctl; /* * Copy ccw address if it is valid. This is a bit simplified * but should be close enough for all practical purposes. */ - if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) || - ((irb->scsw.stctl == + if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) || + ((irb->scsw.cmd.stctl == (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND)) && - (irb->scsw.actl & SCSW_ACTL_DEVACT) && - (irb->scsw.actl & SCSW_ACTL_SCHACT)) || - (irb->scsw.actl & SCSW_ACTL_SUSPENDED)) - cdev_irb->scsw.cpa = irb->scsw.cpa; + (irb->scsw.cmd.actl & SCSW_ACTL_DEVACT) && + (irb->scsw.cmd.actl & SCSW_ACTL_SCHACT)) || + (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)) + cdev_irb->scsw.cmd.cpa = irb->scsw.cmd.cpa; /* Accumulate device status, but not the device busy flag. */ - cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY; + cdev_irb->scsw.cmd.dstat &= ~DEV_STAT_BUSY; /* dstat is not always valid. */ - if (irb->scsw.stctl & + if (irb->scsw.cmd.stctl & (SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS | SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS)) - cdev_irb->scsw.dstat |= irb->scsw.dstat; + cdev_irb->scsw.cmd.dstat |= irb->scsw.cmd.dstat; /* Accumulate subchannel status. */ - cdev_irb->scsw.cstat |= irb->scsw.cstat; + cdev_irb->scsw.cmd.cstat |= irb->scsw.cmd.cstat; /* Copy residual count if it is valid. */ - if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) && - (irb->scsw.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) == 0) - cdev_irb->scsw.count = irb->scsw.count; + if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) && + (irb->scsw.cmd.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) + == 0) + cdev_irb->scsw.cmd.count = irb->scsw.cmd.count; /* Take care of bits in the extended status word. */ ccw_device_accumulate_esw(cdev, irb); @@ -299,7 +298,7 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) * sense facility available/supported when enabling the * concurrent sense facility. */ - if ((cdev_irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && + if ((cdev_irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && !(cdev_irb->esw.esw0.erw.cons)) cdev->private->flags.dosense = 1; } @@ -317,7 +316,7 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) sch = to_subchannel(cdev->dev.parent); /* A sense is required, can we do it now ? */ - if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0) + if (scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) /* * we received an Unit Check but we have no final * status yet, therefore we must delay the SENSE @@ -355,20 +354,18 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb) * If not, the remaining bit have no meaning and we must ignore them. * The esw is not meaningful as well... */ - if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) + if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) return; /* Check for channel checks and interface control checks. */ ccw_device_msg_control_check(cdev, irb); /* Check for path not operational. */ - if (irb->scsw.pno && irb->scsw.fctl != 0 && - (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) || - (irb->scsw.actl & SCSW_ACTL_SUSPENDED))) + if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw)) ccw_device_path_notoper(cdev); - if (!(irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && - (irb->scsw.dstat & DEV_STAT_CHN_END)) { + if (!(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) && + (irb->scsw.cmd.dstat & DEV_STAT_CHN_END)) { cdev->private->irb.esw.esw0.erw.cons = 1; cdev->private->flags.dosense = 0; } @@ -386,11 +383,11 @@ int ccw_device_accumulate_and_sense(struct ccw_device *cdev, struct irb *irb) { ccw_device_accumulate_irb(cdev, irb); - if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0) + if ((irb->scsw.cmd.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0) return -EBUSY; /* Check for basic sense. */ if (cdev->private->flags.dosense && - !(irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) { + !(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)) { cdev->private->irb.esw.esw0.erw.cons = 1; cdev->private->flags.dosense = 0; return 0; diff --git a/drivers/s390/cio/fcx.c b/drivers/s390/cio/fcx.c new file mode 100644 index 00000000000..61677dfbdc9 --- /dev/null +++ b/drivers/s390/cio/fcx.c @@ -0,0 +1,350 @@ +/* + * Functions for assembling fcx enabled I/O control blocks. + * + * Copyright IBM Corp. 2008 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/module.h> +#include <asm/fcx.h> +#include "cio.h" + +/** + * tcw_get_intrg - return pointer to associated interrogate tcw + * @tcw: pointer to the original tcw + * + * Return a pointer to the interrogate tcw associated with the specified tcw + * or %NULL if there is no associated interrogate tcw. + */ +struct tcw *tcw_get_intrg(struct tcw *tcw) +{ + return (struct tcw *) ((addr_t) tcw->intrg); +} +EXPORT_SYMBOL(tcw_get_intrg); + +/** + * tcw_get_data - return pointer to input/output data associated with tcw + * @tcw: pointer to the tcw + * + * Return the input or output data address specified in the tcw depending + * on whether the r-bit or the w-bit is set. If neither bit is set, return + * %NULL. + */ +void *tcw_get_data(struct tcw *tcw) +{ + if (tcw->r) + return (void *) ((addr_t) tcw->input); + if (tcw->w) + return (void *) ((addr_t) tcw->output); + return NULL; +} +EXPORT_SYMBOL(tcw_get_data); + +/** + * tcw_get_tccb - return pointer to tccb associated with tcw + * @tcw: pointer to the tcw + * + * Return pointer to the tccb associated with this tcw. + */ +struct tccb *tcw_get_tccb(struct tcw *tcw) +{ + return (struct tccb *) ((addr_t) tcw->tccb); +} +EXPORT_SYMBOL(tcw_get_tccb); + +/** + * tcw_get_tsb - return pointer to tsb associated with tcw + * @tcw: pointer to the tcw + * + * Return pointer to the tsb associated with this tcw. + */ +struct tsb *tcw_get_tsb(struct tcw *tcw) +{ + return (struct tsb *) ((addr_t) tcw->tsb); +} +EXPORT_SYMBOL(tcw_get_tsb); + +/** + * tcw_init - initialize tcw data structure + * @tcw: pointer to the tcw to be initialized + * @r: initial value of the r-bit + * @w: initial value of the w-bit + * + * Initialize all fields of the specified tcw data structure with zero and + * fill in the format, flags, r and w fields. + */ +void tcw_init(struct tcw *tcw, int r, int w) +{ + memset(tcw, 0, sizeof(struct tcw)); + tcw->format = TCW_FORMAT_DEFAULT; + tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT); + if (r) + tcw->r = 1; + if (w) + tcw->w = 1; +} +EXPORT_SYMBOL(tcw_init); + +static inline size_t tca_size(struct tccb *tccb) +{ + return tccb->tcah.tcal - 12; +} + +static u32 calc_dcw_count(struct tccb *tccb) +{ + int offset; + struct dcw *dcw; + u32 count = 0; + size_t size; + + size = tca_size(tccb); + for (offset = 0; offset < size;) { + dcw = (struct dcw *) &tccb->tca[offset]; + count += dcw->count; + if (!(dcw->flags & DCW_FLAGS_CC)) + break; + offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4); + } + return count; +} + +static u32 calc_cbc_size(struct tidaw *tidaw, int num) +{ + int i; + u32 cbc_data; + u32 cbc_count = 0; + u64 data_count = 0; + + for (i = 0; i < num; i++) { + if (tidaw[i].flags & TIDAW_FLAGS_LAST) + break; + /* TODO: find out if padding applies to total of data + * transferred or data transferred by this tidaw. Assumption: + * applies to total. */ + data_count += tidaw[i].count; + if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) { + cbc_data = 4 + ALIGN(data_count, 4) - data_count; + cbc_count += cbc_data; + data_count += cbc_data; + } + } + return cbc_count; +} + +/** + * tcw_finalize - finalize tcw length fields and tidaw list + * @tcw: pointer to the tcw + * @num_tidaws: the number of tidaws used to address input/output data or zero + * if no tida is used + * + * Calculate the input-/output-count and tccbl field in the tcw, add a + * tcat the tccb and terminate the data tidaw list if used. + * + * Note: in case input- or output-tida is used, the tidaw-list must be stored + * in contiguous storage (no ttic). The tcal field in the tccb must be + * up-to-date. + */ +void tcw_finalize(struct tcw *tcw, int num_tidaws) +{ + struct tidaw *tidaw; + struct tccb *tccb; + struct tccb_tcat *tcat; + u32 count; + + /* Terminate tidaw list. */ + tidaw = tcw_get_data(tcw); + if (num_tidaws > 0) + tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST; + /* Add tcat to tccb. */ + tccb = tcw_get_tccb(tcw); + tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)]; + memset(tcat, 0, sizeof(tcat)); + /* Calculate tcw input/output count and tcat transport count. */ + count = calc_dcw_count(tccb); + if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA)) + count += calc_cbc_size(tidaw, num_tidaws); + if (tcw->r) + tcw->input_count = count; + else if (tcw->w) + tcw->output_count = count; + tcat->count = ALIGN(count, 4) + 4; + /* Calculate tccbl. */ + tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) + + sizeof(struct tccb_tcat) - 20) >> 2; +} +EXPORT_SYMBOL(tcw_finalize); + +/** + * tcw_set_intrg - set the interrogate tcw address of a tcw + * @tcw: the tcw address + * @intrg_tcw: the address of the interrogate tcw + * + * Set the address of the interrogate tcw in the specified tcw. + */ +void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw) +{ + tcw->intrg = (u32) ((addr_t) intrg_tcw); +} +EXPORT_SYMBOL(tcw_set_intrg); + +/** + * tcw_set_data - set data address and tida flag of a tcw + * @tcw: the tcw address + * @data: the data address + * @use_tidal: zero of the data address specifies a contiguous block of data, + * non-zero if it specifies a list if tidaws. + * + * Set the input/output data address of a tcw (depending on the value of the + * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag + * is set as well. + */ +void tcw_set_data(struct tcw *tcw, void *data, int use_tidal) +{ + if (tcw->r) { + tcw->input = (u64) ((addr_t) data); + if (use_tidal) + tcw->flags |= TCW_FLAGS_INPUT_TIDA; + } else if (tcw->w) { + tcw->output = (u64) ((addr_t) data); + if (use_tidal) + tcw->flags |= TCW_FLAGS_OUTPUT_TIDA; + } +} +EXPORT_SYMBOL(tcw_set_data); + +/** + * tcw_set_tccb - set tccb address of a tcw + * @tcw: the tcw address + * @tccb: the tccb address + * + * Set the address of the tccb in the specified tcw. + */ +void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb) +{ + tcw->tccb = (u64) ((addr_t) tccb); +} +EXPORT_SYMBOL(tcw_set_tccb); + +/** + * tcw_set_tsb - set tsb address of a tcw + * @tcw: the tcw address + * @tsb: the tsb address + * + * Set the address of the tsb in the specified tcw. + */ +void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb) +{ + tcw->tsb = (u64) ((addr_t) tsb); +} +EXPORT_SYMBOL(tcw_set_tsb); + +/** + * tccb_init - initialize tccb + * @tccb: the tccb address + * @size: the maximum size of the tccb + * @sac: the service-action-code to be user + * + * Initialize the header of the specified tccb by resetting all values to zero + * and filling in defaults for format, sac and initial tcal fields. + */ +void tccb_init(struct tccb *tccb, size_t size, u32 sac) +{ + memset(tccb, 0, size); + tccb->tcah.format = TCCB_FORMAT_DEFAULT; + tccb->tcah.sac = sac; + tccb->tcah.tcal = 12; +} +EXPORT_SYMBOL(tccb_init); + +/** + * tsb_init - initialize tsb + * @tsb: the tsb address + * + * Initialize the specified tsb by resetting all values to zero. + */ +void tsb_init(struct tsb *tsb) +{ + memset(tsb, 0, sizeof(tsb)); +} +EXPORT_SYMBOL(tsb_init); + +/** + * tccb_add_dcw - add a dcw to the tccb + * @tccb: the tccb address + * @tccb_size: the maximum tccb size + * @cmd: the dcw command + * @flags: flags for the dcw + * @cd: pointer to control data for this dcw or NULL if none is required + * @cd_count: number of control data bytes for this dcw + * @count: number of data bytes for this dcw + * + * Add a new dcw to the specified tccb by writing the dcw information specified + * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return + * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw + * would exceed the available space as defined by @tccb_size. + * + * Note: the tcal field of the tccb header will be updates to reflect added + * content. + */ +struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags, + void *cd, u8 cd_count, u32 count) +{ + struct dcw *dcw; + int size; + int tca_offset; + + /* Check for space. */ + tca_offset = tca_size(tccb); + size = ALIGN(sizeof(struct dcw) + cd_count, 4); + if (sizeof(struct tccb_tcah) + tca_offset + size + + sizeof(struct tccb_tcat) > tccb_size) + return ERR_PTR(-ENOSPC); + /* Add dcw to tca. */ + dcw = (struct dcw *) &tccb->tca[tca_offset]; + memset(dcw, 0, size); + dcw->cmd = cmd; + dcw->flags = flags; + dcw->count = count; + dcw->cd_count = cd_count; + if (cd) + memcpy(&dcw->cd[0], cd, cd_count); + tccb->tcah.tcal += size; + return dcw; +} +EXPORT_SYMBOL(tccb_add_dcw); + +/** + * tcw_add_tidaw - add a tidaw to a tcw + * @tcw: the tcw address + * @num_tidaws: the current number of tidaws + * @flags: flags for the new tidaw + * @addr: address value for the new tidaw + * @count: count value for the new tidaw + * + * Add a new tidaw to the input/output data tidaw-list of the specified tcw + * (depending on the value of the r-flag and w-flag) and return a pointer to + * the new tidaw. + * + * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller + * must ensure that there is enough space for the new tidaw. The last-tidaw + * flag for the last tidaw in the list will be set by tcw_finalize. + */ +struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags, + void *addr, u32 count) +{ + struct tidaw *tidaw; + + /* Add tidaw to tidaw-list. */ + tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws; + memset(tidaw, 0, sizeof(struct tidaw)); + tidaw->flags = flags; + tidaw->count = count; + tidaw->addr = (u64) ((addr_t) addr); + return tidaw; +} +EXPORT_SYMBOL(tcw_add_tidaw); diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h index 144466ab8c1..528065cb502 100644 --- a/drivers/s390/cio/idset.h +++ b/drivers/s390/cio/idset.h @@ -8,7 +8,7 @@ #ifndef S390_IDSET_H #define S390_IDSET_H S390_IDSET_H -#include "schid.h" +#include <asm/schid.h> struct idset; diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 8c613160bfc..3f8f1cf69c7 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -1,12 +1,12 @@ #ifndef S390_IO_SCH_H #define S390_IO_SCH_H -#include "schid.h" +#include <asm/schid.h> /* - * operation request block + * command-mode operation request block */ -struct orb { +struct cmd_orb { u32 intparm; /* interruption parameter */ u32 key : 4; /* flags, like key, suspend control, etc. */ u32 spnd : 1; /* suspend control */ @@ -28,8 +28,36 @@ struct orb { u32 cpa; /* channel program address */ } __attribute__ ((packed, aligned(4))); +/* + * transport-mode operation request block + */ +struct tm_orb { + u32 intparm; + u32 key:4; + u32 :9; + u32 b:1; + u32 :2; + u32 lpm:8; + u32 :7; + u32 x:1; + u32 tcw; + u32 prio:8; + u32 :8; + u32 rsvpgm:8; + u32 :8; + u32 :32; + u32 :32; + u32 :32; + u32 :32; +} __attribute__ ((packed, aligned(4))); + +union orb { + struct cmd_orb cmd; + struct tm_orb tm; +} __attribute__ ((packed, aligned(4))); + struct io_subchannel_private { - struct orb orb; /* operation request block */ + union orb orb; /* operation request block */ struct ccw1 sense_ccw; /* static ccw for sense command */ } __attribute__ ((aligned(8))); @@ -95,16 +123,18 @@ struct ccw_device_private { void *cmb_wait; /* deferred cmb enable/disable */ }; -static inline int ssch(struct subchannel_id schid, volatile struct orb *addr) +static inline int ssch(struct subchannel_id schid, volatile union orb *addr) { register struct subchannel_id reg1 asm("1") = schid; - int ccode; + int ccode = -EIO; asm volatile( " ssch 0(%2)\n" - " ipm %0\n" - " srl %0,28" - : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); + "0: ipm %0\n" + " srl %0,28\n" + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc"); return ccode; } diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 652ea3625f9..9fa2ac13ac8 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -2,7 +2,7 @@ #define S390_CIO_IOASM_H #include <asm/chpid.h> -#include "schid.h" +#include <asm/schid.h> /* * TPI info structure diff --git a/drivers/s390/cio/isc.c b/drivers/s390/cio/isc.c new file mode 100644 index 00000000000..c592087be0f --- /dev/null +++ b/drivers/s390/cio/isc.c @@ -0,0 +1,68 @@ +/* + * Functions for registration of I/O interruption subclasses on s390. + * + * Copyright IBM Corp. 2008 + * Authors: Sebastian Ott <sebott@linux.vnet.ibm.com> + */ + +#include <linux/spinlock.h> +#include <linux/module.h> +#include <asm/isc.h> + +static unsigned int isc_refs[MAX_ISC + 1]; +static DEFINE_SPINLOCK(isc_ref_lock); + + +/** + * isc_register - register an I/O interruption subclass. + * @isc: I/O interruption subclass to register + * + * The number of users for @isc is increased. If this is the first user to + * register @isc, the corresponding I/O interruption subclass mask is enabled. + * + * Context: + * This function must not be called in interrupt context. + */ +void isc_register(unsigned int isc) +{ + if (isc > MAX_ISC) { + WARN_ON(1); + return; + } + + spin_lock(&isc_ref_lock); + if (isc_refs[isc] == 0) + ctl_set_bit(6, 31 - isc); + isc_refs[isc]++; + spin_unlock(&isc_ref_lock); +} +EXPORT_SYMBOL_GPL(isc_register); + +/** + * isc_unregister - unregister an I/O interruption subclass. + * @isc: I/O interruption subclass to unregister + * + * The number of users for @isc is decreased. If this is the last user to + * unregister @isc, the corresponding I/O interruption subclass mask is + * disabled. + * Note: This function must not be called if isc_register() hasn't been called + * before by the driver for @isc. + * + * Context: + * This function must not be called in interrupt context. + */ +void isc_unregister(unsigned int isc) +{ + spin_lock(&isc_ref_lock); + /* check for misuse */ + if (isc > MAX_ISC || isc_refs[isc] == 0) { + WARN_ON(1); + goto out_unlock; + } + if (isc_refs[isc] == 1) + ctl_clear_bit(6, 31 - isc); + isc_refs[isc]--; +out_unlock: + spin_unlock(&isc_ref_lock); +} +EXPORT_SYMBOL_GPL(isc_unregister); diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c new file mode 100644 index 00000000000..17da9ab932e --- /dev/null +++ b/drivers/s390/cio/itcw.c @@ -0,0 +1,327 @@ +/* + * Functions for incremental construction of fcx enabled I/O control blocks. + * + * Copyright IBM Corp. 2008 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/module.h> +#include <asm/fcx.h> +#include <asm/itcw.h> + +/** + * struct itcw - incremental tcw helper data type + * + * This structure serves as a handle for the incremental construction of a + * tcw and associated tccb, tsb, data tidaw-list plus an optional interrogate + * tcw and associated data. The data structures are contained inside a single + * contiguous buffer provided by the user. + * + * The itcw construction functions take care of overall data integrity: + * - reset unused fields to zero + * - fill in required pointers + * - ensure required alignment for data structures + * - prevent data structures to cross 4k-byte boundary where required + * - calculate tccb-related length fields + * - optionally provide ready-made interrogate tcw and associated structures + * + * Restrictions apply to the itcws created with these construction functions: + * - tida only supported for data address, not for tccb + * - only contiguous tidaw-lists (no ttic) + * - total number of bytes required per itcw may not exceed 4k bytes + * - either read or write operation (may not work with r=0 and w=0) + * + * Example: + * struct itcw *itcw; + * void *buffer; + * size_t size; + * + * size = itcw_calc_size(1, 2, 0); + * buffer = kmalloc(size, GFP_DMA); + * if (!buffer) + * return -ENOMEM; + * itcw = itcw_init(buffer, size, ITCW_OP_READ, 1, 2, 0); + * if (IS_ERR(itcw)) + * return PTR_ER(itcw); + * itcw_add_dcw(itcw, 0x2, 0, NULL, 0, 72); + * itcw_add_tidaw(itcw, 0, 0x30000, 20); + * itcw_add_tidaw(itcw, 0, 0x40000, 52); + * itcw_finalize(itcw); + * + */ +struct itcw { + struct tcw *tcw; + struct tcw *intrg_tcw; + int num_tidaws; + int max_tidaws; + int intrg_num_tidaws; + int intrg_max_tidaws; +}; + +/** + * itcw_get_tcw - return pointer to tcw associated with the itcw + * @itcw: address of the itcw + * + * Return pointer to the tcw associated with the itcw. + */ +struct tcw *itcw_get_tcw(struct itcw *itcw) +{ + return itcw->tcw; +} +EXPORT_SYMBOL(itcw_get_tcw); + +/** + * itcw_calc_size - return the size of an itcw with the given parameters + * @intrg: if non-zero, add an interrogate tcw + * @max_tidaws: maximum number of tidaws to be used for data addressing or zero + * if no tida is to be used. + * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing + * by the interrogate tcw, if specified + * + * Calculate and return the number of bytes required to hold an itcw with the + * given parameters and assuming tccbs with maximum size. + * + * Note that the resulting size also contains bytes needed for alignment + * padding as well as padding to ensure that data structures don't cross a + * 4k-boundary where required. + */ +size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) +{ + size_t len; + + /* Main data. */ + len = sizeof(struct itcw); + len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE + + /* TSB */ sizeof(struct tsb) + + /* TIDAL */ max_tidaws * sizeof(struct tidaw); + /* Interrogate data. */ + if (intrg) { + len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE + + /* TSB */ sizeof(struct tsb) + + /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); + } + /* Maximum required alignment padding. */ + len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; + /* Maximum padding for structures that may not cross 4k boundary. */ + if ((max_tidaws > 0) || (intrg_max_tidaws > 0)) + len += max(max_tidaws, intrg_max_tidaws) * + sizeof(struct tidaw) - 1; + return len; +} +EXPORT_SYMBOL(itcw_calc_size); + +#define CROSS4K(x, l) (((x) & ~4095) != ((x + l) & ~4095)) + +static inline void *fit_chunk(addr_t *start, addr_t end, size_t len, + int align, int check_4k) +{ + addr_t addr; + + addr = ALIGN(*start, align); + if (check_4k && CROSS4K(addr, len)) { + addr = ALIGN(addr, 4096); + addr = ALIGN(addr, align); + } + if (addr + len > end) + return ERR_PTR(-ENOSPC); + *start = addr + len; + return (void *) addr; +} + +/** + * itcw_init - initialize incremental tcw data structure + * @buffer: address of buffer to use for data structures + * @size: number of bytes in buffer + * @op: %ITCW_OP_READ for a read operation tcw, %ITCW_OP_WRITE for a write + * operation tcw + * @intrg: if non-zero, add and initialize an interrogate tcw + * @max_tidaws: maximum number of tidaws to be used for data addressing or zero + * if no tida is to be used. + * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing + * by the interrogate tcw, if specified + * + * Prepare the specified buffer to be used as an incremental tcw, i.e. a + * helper data structure that can be used to construct a valid tcw by + * successive calls to other helper functions. Note: the buffer needs to be + * located below the 2G address limit. The resulting tcw has the following + * restrictions: + * - no tccb tidal + * - input/output tidal is contiguous (no ttic) + * - total data should not exceed 4k + * - tcw specifies either read or write operation + * + * On success, return pointer to the resulting incremental tcw data structure, + * ERR_PTR otherwise. + */ +struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, + int max_tidaws, int intrg_max_tidaws) +{ + struct itcw *itcw; + void *chunk; + addr_t start; + addr_t end; + + /* Check for 2G limit. */ + start = (addr_t) buffer; + end = start + size; + if (end > (1 << 31)) + return ERR_PTR(-EINVAL); + memset(buffer, 0, size); + /* ITCW. */ + chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0); + if (IS_ERR(chunk)) + return chunk; + itcw = chunk; + itcw->max_tidaws = max_tidaws; + itcw->intrg_max_tidaws = intrg_max_tidaws; + /* Main TCW. */ + chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); + if (IS_ERR(chunk)) + return chunk; + itcw->tcw = chunk; + tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0, + (op == ITCW_OP_WRITE) ? 1 : 0); + /* Interrogate TCW. */ + if (intrg) { + chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); + if (IS_ERR(chunk)) + return chunk; + itcw->intrg_tcw = chunk; + tcw_init(itcw->intrg_tcw, 1, 0); + tcw_set_intrg(itcw->tcw, itcw->intrg_tcw); + } + /* Data TIDAL. */ + if (max_tidaws > 0) { + chunk = fit_chunk(&start, end, sizeof(struct tidaw) * + max_tidaws, 16, 1); + if (IS_ERR(chunk)) + return chunk; + tcw_set_data(itcw->tcw, chunk, 1); + } + /* Interrogate data TIDAL. */ + if (intrg && (intrg_max_tidaws > 0)) { + chunk = fit_chunk(&start, end, sizeof(struct tidaw) * + intrg_max_tidaws, 16, 1); + if (IS_ERR(chunk)) + return chunk; + tcw_set_data(itcw->intrg_tcw, chunk, 1); + } + /* TSB. */ + chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0); + if (IS_ERR(chunk)) + return chunk; + tsb_init(chunk); + tcw_set_tsb(itcw->tcw, chunk); + /* Interrogate TSB. */ + if (intrg) { + chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0); + if (IS_ERR(chunk)) + return chunk; + tsb_init(chunk); + tcw_set_tsb(itcw->intrg_tcw, chunk); + } + /* TCCB. */ + chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0); + if (IS_ERR(chunk)) + return chunk; + tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_DEFAULT); + tcw_set_tccb(itcw->tcw, chunk); + /* Interrogate TCCB. */ + if (intrg) { + chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0); + if (IS_ERR(chunk)) + return chunk; + tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_INTRG); + tcw_set_tccb(itcw->intrg_tcw, chunk); + tccb_add_dcw(chunk, TCCB_MAX_SIZE, DCW_CMD_INTRG, 0, NULL, + sizeof(struct dcw_intrg_data), 0); + tcw_finalize(itcw->intrg_tcw, 0); + } + return itcw; +} +EXPORT_SYMBOL(itcw_init); + +/** + * itcw_add_dcw - add a dcw to the itcw + * @itcw: address of the itcw + * @cmd: the dcw command + * @flags: flags for the dcw + * @cd: address of control data for this dcw or NULL if none is required + * @cd_count: number of control data bytes for this dcw + * @count: number of data bytes for this dcw + * + * Add a new dcw to the specified itcw by writing the dcw information specified + * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return + * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw + * would exceed the available space. + * + * Note: the tcal field of the tccb header will be updated to reflect added + * content. + */ +struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd, + u8 cd_count, u32 count) +{ + return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd, + flags, cd, cd_count, count); +} +EXPORT_SYMBOL(itcw_add_dcw); + +/** + * itcw_add_tidaw - add a tidaw to the itcw + * @itcw: address of the itcw + * @flags: flags for the new tidaw + * @addr: address value for the new tidaw + * @count: count value for the new tidaw + * + * Add a new tidaw to the input/output data tidaw-list of the specified itcw + * (depending on the value of the r-flag and w-flag). Return a pointer to + * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the + * available space. + * + * Note: the tidaw-list is assumed to be contiguous with no ttics. The + * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize. + */ +struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) +{ + if (itcw->num_tidaws >= itcw->max_tidaws) + return ERR_PTR(-ENOSPC); + return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); +} +EXPORT_SYMBOL(itcw_add_tidaw); + +/** + * itcw_set_data - set data address and tida flag of the itcw + * @itcw: address of the itcw + * @addr: the data address + * @use_tidal: zero of the data address specifies a contiguous block of data, + * non-zero if it specifies a list if tidaws. + * + * Set the input/output data address of the itcw (depending on the value of the + * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag + * is set as well. + */ +void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal) +{ + tcw_set_data(itcw->tcw, addr, use_tidal); +} +EXPORT_SYMBOL(itcw_set_data); + +/** + * itcw_finalize - calculate length and count fields of the itcw + * @itcw: address of the itcw + * + * Calculate tcw input-/output-count and tccbl fields and add a tcat the tccb. + * In case input- or output-tida is used, the tidaw-list must be stored in + * continuous storage (no ttic). The tcal field in the tccb must be + * up-to-date. + */ +void itcw_finalize(struct itcw *itcw) +{ + tcw_finalize(itcw->tcw, itcw->num_tidaws); +} +EXPORT_SYMBOL(itcw_finalize); diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 445cf364e46..2bf36e14b10 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -2082,7 +2082,6 @@ qdio_timeout_handler(struct ccw_device *cdev) default: BUG(); } - ccw_device_set_timeout(cdev, 0); wake_up(&cdev->private->wait_q); } @@ -2121,6 +2120,8 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) case -EIO: QDIO_PRINT_ERR("i/o error on device %s\n", cdev->dev.bus_id); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); + wake_up(&cdev->private->wait_q); return; case -ETIMEDOUT: qdio_timeout_handler(cdev); @@ -2139,8 +2140,8 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) QDIO_DBF_TEXT4(0, trace, dbf_text); #endif /* CONFIG_QDIO_DEBUG */ - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; switch (irq_ptr->state) { case QDIO_IRQ_STATE_INACTIVE: @@ -2353,9 +2354,6 @@ tiqdio_check_chsc_availability(void) { char dbf_text[15]; - if (!css_characteristics_avail) - return -EIO; - /* Check for bit 41. */ if (!css_general_characteristics.aif) { QDIO_PRINT_WARN("Adapter interruption facility not " \ @@ -2667,12 +2665,12 @@ qdio_shutdown(struct ccw_device *cdev, int how) spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); } else if (rc == 0) { qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP); - ccw_device_set_timeout(cdev, timeout); spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags); - wait_event(cdev->private->wait_q, - irq_ptr->state == QDIO_IRQ_STATE_INACTIVE || - irq_ptr->state == QDIO_IRQ_STATE_ERR); + wait_event_interruptible_timeout(cdev->private->wait_q, + irq_ptr->state == QDIO_IRQ_STATE_INACTIVE || + irq_ptr->state == QDIO_IRQ_STATE_ERR, + timeout); } else { QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for " "device %s\n", result, cdev->dev.bus_id); @@ -2692,7 +2690,6 @@ qdio_shutdown(struct ccw_device *cdev, int how) /* Ignore errors. */ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); - ccw_device_set_timeout(cdev, 0); out: up(&irq_ptr->setting_up_sema); return result; @@ -2907,13 +2904,10 @@ qdio_establish_handle_irq(struct ccw_device *cdev, int cstat, int dstat) QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,trace,dbf_text); - if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) { - ccw_device_set_timeout(cdev, 0); + if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) return; - } qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED); - ccw_device_set_timeout(cdev, 0); } int @@ -3196,8 +3190,6 @@ qdio_establish(struct qdio_initialize *init_data) irq_ptr->schid.ssid, irq_ptr->schid.sch_no, result, result2); result=result2; - if (result) - ccw_device_set_timeout(cdev, 0); } spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags); @@ -3279,7 +3271,6 @@ qdio_activate(struct ccw_device *cdev, int flags) spin_lock_irqsave(get_ccwdev_lock(cdev),saveflags); - ccw_device_set_timeout(cdev, 0); ccw_device_set_options(cdev, CCWDEV_REPORT_ALL); result=ccw_device_start(cdev,&irq_ptr->ccw,QDIO_DOING_ACTIVATE, 0, DOIO_DENY_PREFETCH); @@ -3722,7 +3713,8 @@ tiqdio_register_thinints(void) char dbf_text[20]; tiqdio_ind = - s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL); + s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL, + TIQDIO_THININT_ISC); if (IS_ERR(tiqdio_ind)) { sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind)); QDIO_DBF_TEXT0(0,setup,dbf_text); @@ -3738,7 +3730,8 @@ static void tiqdio_unregister_thinints(void) { if (tiqdio_ind) - s390_unregister_adapter_interrupt(tiqdio_ind); + s390_unregister_adapter_interrupt(tiqdio_ind, + TIQDIO_THININT_ISC); } static int @@ -3899,6 +3892,7 @@ init_QDIO(void) qdio_mempool_alloc, qdio_mempool_free, NULL); + isc_register(QDIO_AIRQ_ISC); if (tiqdio_check_chsc_availability()) QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n"); @@ -3911,6 +3905,7 @@ static void __exit cleanup_QDIO(void) { tiqdio_unregister_thinints(); + isc_unregister(QDIO_AIRQ_ISC); qdio_remove_procfs_entry(); qdio_release_qdio_memory(); qdio_unregister_dbf_views(); diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index c3df6b2c38b..7656081a24d 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -2,8 +2,8 @@ #define _CIO_QDIO_H #include <asm/page.h> - -#include "schid.h" +#include <asm/isc.h> +#include <asm/schid.h> #ifdef CONFIG_QDIO_DEBUG #define QDIO_VERBOSE_LEVEL 9 @@ -26,7 +26,7 @@ */ #define IQDIO_FILL_LEVEL_TO_POLL 4 -#define TIQDIO_THININT_ISC 3 +#define TIQDIO_THININT_ISC QDIO_AIRQ_ISC #define TIQDIO_DELAY_TARGET 0 #define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */ #define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */ diff --git a/drivers/s390/cio/scsw.c b/drivers/s390/cio/scsw.c new file mode 100644 index 00000000000..f8da25ab576 --- /dev/null +++ b/drivers/s390/cio/scsw.c @@ -0,0 +1,843 @@ +/* + * Helper functions for scsw access. + * + * Copyright IBM Corp. 2008 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <asm/cio.h> +#include "css.h" +#include "chsc.h" + +/** + * scsw_is_tm - check for transport mode scsw + * @scsw: pointer to scsw + * + * Return non-zero if the specified scsw is a transport mode scsw, zero + * otherwise. + */ +int scsw_is_tm(union scsw *scsw) +{ + return css_general_characteristics.fcx && (scsw->tm.x == 1); +} +EXPORT_SYMBOL(scsw_is_tm); + +/** + * scsw_key - return scsw key field + * @scsw: pointer to scsw + * + * Return the value of the key field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_key(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.key; + else + return scsw->cmd.key; +} +EXPORT_SYMBOL(scsw_key); + +/** + * scsw_eswf - return scsw eswf field + * @scsw: pointer to scsw + * + * Return the value of the eswf field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_eswf(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.eswf; + else + return scsw->cmd.eswf; +} +EXPORT_SYMBOL(scsw_eswf); + +/** + * scsw_cc - return scsw cc field + * @scsw: pointer to scsw + * + * Return the value of the cc field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_cc(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.cc; + else + return scsw->cmd.cc; +} +EXPORT_SYMBOL(scsw_cc); + +/** + * scsw_ectl - return scsw ectl field + * @scsw: pointer to scsw + * + * Return the value of the ectl field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_ectl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.ectl; + else + return scsw->cmd.ectl; +} +EXPORT_SYMBOL(scsw_ectl); + +/** + * scsw_pno - return scsw pno field + * @scsw: pointer to scsw + * + * Return the value of the pno field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_pno(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.pno; + else + return scsw->cmd.pno; +} +EXPORT_SYMBOL(scsw_pno); + +/** + * scsw_fctl - return scsw fctl field + * @scsw: pointer to scsw + * + * Return the value of the fctl field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_fctl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.fctl; + else + return scsw->cmd.fctl; +} +EXPORT_SYMBOL(scsw_fctl); + +/** + * scsw_actl - return scsw actl field + * @scsw: pointer to scsw + * + * Return the value of the actl field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_actl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.actl; + else + return scsw->cmd.actl; +} +EXPORT_SYMBOL(scsw_actl); + +/** + * scsw_stctl - return scsw stctl field + * @scsw: pointer to scsw + * + * Return the value of the stctl field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_stctl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.stctl; + else + return scsw->cmd.stctl; +} +EXPORT_SYMBOL(scsw_stctl); + +/** + * scsw_dstat - return scsw dstat field + * @scsw: pointer to scsw + * + * Return the value of the dstat field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_dstat(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.dstat; + else + return scsw->cmd.dstat; +} +EXPORT_SYMBOL(scsw_dstat); + +/** + * scsw_cstat - return scsw cstat field + * @scsw: pointer to scsw + * + * Return the value of the cstat field of the specified scsw, regardless of + * whether it is a transport mode or command mode scsw. + */ +u32 scsw_cstat(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw->tm.cstat; + else + return scsw->cmd.cstat; +} +EXPORT_SYMBOL(scsw_cstat); + +/** + * scsw_cmd_is_valid_key - check key field validity + * @scsw: pointer to scsw + * + * Return non-zero if the key field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_key(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_key); + +/** + * scsw_cmd_is_valid_sctl - check fctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fctl field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_sctl(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_sctl); + +/** + * scsw_cmd_is_valid_eswf - check eswf field validity + * @scsw: pointer to scsw + * + * Return non-zero if the eswf field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_eswf(union scsw *scsw) +{ + return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_eswf); + +/** + * scsw_cmd_is_valid_cc - check cc field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cc field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_cc(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && + (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_cc); + +/** + * scsw_cmd_is_valid_fmt - check fmt field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fmt field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_fmt(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_fmt); + +/** + * scsw_cmd_is_valid_pfch - check pfch field validity + * @scsw: pointer to scsw + * + * Return non-zero if the pfch field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_pfch(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_pfch); + +/** + * scsw_cmd_is_valid_isic - check isic field validity + * @scsw: pointer to scsw + * + * Return non-zero if the isic field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_isic(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_isic); + +/** + * scsw_cmd_is_valid_alcc - check alcc field validity + * @scsw: pointer to scsw + * + * Return non-zero if the alcc field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_alcc(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_alcc); + +/** + * scsw_cmd_is_valid_ssi - check ssi field validity + * @scsw: pointer to scsw + * + * Return non-zero if the ssi field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_ssi(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_ssi); + +/** + * scsw_cmd_is_valid_zcc - check zcc field validity + * @scsw: pointer to scsw + * + * Return non-zero if the zcc field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_zcc(union scsw *scsw) +{ + return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && + (scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_zcc); + +/** + * scsw_cmd_is_valid_ectl - check ectl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the ectl field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_ectl(union scsw *scsw) +{ + return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && + !(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) && + (scsw->cmd.stctl & SCSW_STCTL_ALERT_STATUS); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_ectl); + +/** + * scsw_cmd_is_valid_pno - check pno field validity + * @scsw: pointer to scsw + * + * Return non-zero if the pno field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_pno(union scsw *scsw) +{ + return (scsw->cmd.fctl != 0) && + (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && + (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) || + ((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) && + (scsw->cmd.actl & SCSW_ACTL_SUSPENDED))); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_pno); + +/** + * scsw_cmd_is_valid_fctl - check fctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fctl field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_fctl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_cmd_is_valid_fctl); + +/** + * scsw_cmd_is_valid_actl - check actl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the actl field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_actl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_cmd_is_valid_actl); + +/** + * scsw_cmd_is_valid_stctl - check stctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the stctl field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_stctl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_cmd_is_valid_stctl); + +/** + * scsw_cmd_is_valid_dstat - check dstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the dstat field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_dstat(union scsw *scsw) +{ + return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && + (scsw->cmd.cc != 3); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_dstat); + +/** + * scsw_cmd_is_valid_cstat - check cstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cstat field of the specified command mode scsw is + * valid, zero otherwise. + */ +int scsw_cmd_is_valid_cstat(union scsw *scsw) +{ + return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && + (scsw->cmd.cc != 3); +} +EXPORT_SYMBOL(scsw_cmd_is_valid_cstat); + +/** + * scsw_tm_is_valid_key - check key field validity + * @scsw: pointer to scsw + * + * Return non-zero if the key field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_key(union scsw *scsw) +{ + return (scsw->tm.fctl & SCSW_FCTL_START_FUNC); +} +EXPORT_SYMBOL(scsw_tm_is_valid_key); + +/** + * scsw_tm_is_valid_eswf - check eswf field validity + * @scsw: pointer to scsw + * + * Return non-zero if the eswf field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_eswf(union scsw *scsw) +{ + return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND); +} +EXPORT_SYMBOL(scsw_tm_is_valid_eswf); + +/** + * scsw_tm_is_valid_cc - check cc field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cc field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_cc(union scsw *scsw) +{ + return (scsw->tm.fctl & SCSW_FCTL_START_FUNC) && + (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND); +} +EXPORT_SYMBOL(scsw_tm_is_valid_cc); + +/** + * scsw_tm_is_valid_fmt - check fmt field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fmt field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_fmt(union scsw *scsw) +{ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_fmt); + +/** + * scsw_tm_is_valid_x - check x field validity + * @scsw: pointer to scsw + * + * Return non-zero if the x field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_x(union scsw *scsw) +{ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_x); + +/** + * scsw_tm_is_valid_q - check q field validity + * @scsw: pointer to scsw + * + * Return non-zero if the q field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_q(union scsw *scsw) +{ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_q); + +/** + * scsw_tm_is_valid_ectl - check ectl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the ectl field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_ectl(union scsw *scsw) +{ + return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && + !(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) && + (scsw->tm.stctl & SCSW_STCTL_ALERT_STATUS); +} +EXPORT_SYMBOL(scsw_tm_is_valid_ectl); + +/** + * scsw_tm_is_valid_pno - check pno field validity + * @scsw: pointer to scsw + * + * Return non-zero if the pno field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_pno(union scsw *scsw) +{ + return (scsw->tm.fctl != 0) && + (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && + (!(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) || + ((scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) && + (scsw->tm.actl & SCSW_ACTL_SUSPENDED))); +} +EXPORT_SYMBOL(scsw_tm_is_valid_pno); + +/** + * scsw_tm_is_valid_fctl - check fctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fctl field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_fctl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_fctl); + +/** + * scsw_tm_is_valid_actl - check actl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the actl field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_actl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_actl); + +/** + * scsw_tm_is_valid_stctl - check stctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the stctl field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_stctl(union scsw *scsw) +{ + /* Only valid if pmcw.dnv == 1*/ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_stctl); + +/** + * scsw_tm_is_valid_dstat - check dstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the dstat field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_dstat(union scsw *scsw) +{ + return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && + (scsw->tm.cc != 3); +} +EXPORT_SYMBOL(scsw_tm_is_valid_dstat); + +/** + * scsw_tm_is_valid_cstat - check cstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cstat field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_cstat(union scsw *scsw) +{ + return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) && + (scsw->tm.cc != 3); +} +EXPORT_SYMBOL(scsw_tm_is_valid_cstat); + +/** + * scsw_tm_is_valid_fcxs - check fcxs field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fcxs field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_fcxs(union scsw *scsw) +{ + return 1; +} +EXPORT_SYMBOL(scsw_tm_is_valid_fcxs); + +/** + * scsw_tm_is_valid_schxs - check schxs field validity + * @scsw: pointer to scsw + * + * Return non-zero if the schxs field of the specified transport mode scsw is + * valid, zero otherwise. + */ +int scsw_tm_is_valid_schxs(union scsw *scsw) +{ + return (scsw->tm.cstat & (SCHN_STAT_PROG_CHECK | + SCHN_STAT_INTF_CTRL_CHK | + SCHN_STAT_PROT_CHECK | + SCHN_STAT_CHN_DATA_CHK)); +} +EXPORT_SYMBOL(scsw_tm_is_valid_schxs); + +/** + * scsw_is_valid_actl - check actl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the actl field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_actl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_actl(scsw); + else + return scsw_cmd_is_valid_actl(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_actl); + +/** + * scsw_is_valid_cc - check cc field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cc field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_cc(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_cc(scsw); + else + return scsw_cmd_is_valid_cc(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_cc); + +/** + * scsw_is_valid_cstat - check cstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the cstat field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_cstat(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_cstat(scsw); + else + return scsw_cmd_is_valid_cstat(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_cstat); + +/** + * scsw_is_valid_dstat - check dstat field validity + * @scsw: pointer to scsw + * + * Return non-zero if the dstat field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_dstat(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_dstat(scsw); + else + return scsw_cmd_is_valid_dstat(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_dstat); + +/** + * scsw_is_valid_ectl - check ectl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the ectl field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_ectl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_ectl(scsw); + else + return scsw_cmd_is_valid_ectl(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_ectl); + +/** + * scsw_is_valid_eswf - check eswf field validity + * @scsw: pointer to scsw + * + * Return non-zero if the eswf field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_eswf(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_eswf(scsw); + else + return scsw_cmd_is_valid_eswf(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_eswf); + +/** + * scsw_is_valid_fctl - check fctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the fctl field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_fctl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_fctl(scsw); + else + return scsw_cmd_is_valid_fctl(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_fctl); + +/** + * scsw_is_valid_key - check key field validity + * @scsw: pointer to scsw + * + * Return non-zero if the key field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_key(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_key(scsw); + else + return scsw_cmd_is_valid_key(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_key); + +/** + * scsw_is_valid_pno - check pno field validity + * @scsw: pointer to scsw + * + * Return non-zero if the pno field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_pno(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_pno(scsw); + else + return scsw_cmd_is_valid_pno(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_pno); + +/** + * scsw_is_valid_stctl - check stctl field validity + * @scsw: pointer to scsw + * + * Return non-zero if the stctl field of the specified scsw is valid, + * regardless of whether it is a transport mode or command mode scsw. + * Return zero if the field does not contain a valid value. + */ +int scsw_is_valid_stctl(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_valid_stctl(scsw); + else + return scsw_cmd_is_valid_stctl(scsw); +} +EXPORT_SYMBOL(scsw_is_valid_stctl); + +/** + * scsw_cmd_is_solicited - check for solicited scsw + * @scsw: pointer to scsw + * + * Return non-zero if the command mode scsw indicates that the associated + * status condition is solicited, zero if it is unsolicited. + */ +int scsw_cmd_is_solicited(union scsw *scsw) +{ + return (scsw->cmd.cc != 0) || (scsw->cmd.stctl != + (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)); +} +EXPORT_SYMBOL(scsw_cmd_is_solicited); + +/** + * scsw_tm_is_solicited - check for solicited scsw + * @scsw: pointer to scsw + * + * Return non-zero if the transport mode scsw indicates that the associated + * status condition is solicited, zero if it is unsolicited. + */ +int scsw_tm_is_solicited(union scsw *scsw) +{ + return (scsw->tm.cc != 0) || (scsw->tm.stctl != + (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)); +} +EXPORT_SYMBOL(scsw_tm_is_solicited); + +/** + * scsw_is_solicited - check for solicited scsw + * @scsw: pointer to scsw + * + * Return non-zero if the transport or command mode scsw indicates that the + * associated status condition is solicited, zero if it is unsolicited. + */ +int scsw_is_solicited(union scsw *scsw) +{ + if (scsw_is_tm(scsw)) + return scsw_tm_is_solicited(scsw); + else + return scsw_cmd_is_solicited(scsw); +} +EXPORT_SYMBOL(scsw_is_solicited); diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index a1ab3e3efd1..62b6b55230d 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -34,13 +34,15 @@ #include <linux/mutex.h> #include <asm/s390_rdev.h> #include <asm/reset.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> #include "ap_bus.h" /* Some prototypes. */ static void ap_scan_bus(struct work_struct *); static void ap_poll_all(unsigned long); -static void ap_poll_timeout(unsigned long); +static enum hrtimer_restart ap_poll_timeout(struct hrtimer *); static int ap_poll_thread_start(void); static void ap_poll_thread_stop(void); static void ap_request_timeout(unsigned long); @@ -80,12 +82,15 @@ static DECLARE_WORK(ap_config_work, ap_scan_bus); /* * Tasklet & timer for AP request polling. */ -static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0); static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); static atomic_t ap_poll_requests = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); +static struct hrtimer ap_poll_timer; +/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. + * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ +static unsigned long long poll_timeout = 250000; /** * ap_intructions_available() - Test if AP instructions are available. @@ -636,11 +641,39 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus, static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store); +static ssize_t poll_timeout_show(struct bus_type *bus, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%llu\n", poll_timeout); +} + +static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf, + size_t count) +{ + unsigned long long time; + ktime_t hr_time; + + /* 120 seconds = maximum poll interval */ + if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000) + return -EINVAL; + poll_timeout = time; + hr_time = ktime_set(0, poll_timeout); + + if (!hrtimer_is_queued(&ap_poll_timer) || + !hrtimer_forward(&ap_poll_timer, ap_poll_timer.expires, hr_time)) { + ap_poll_timer.expires = hr_time; + hrtimer_start(&ap_poll_timer, hr_time, HRTIMER_MODE_ABS); + } + return count; +} + +static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store); + static struct bus_attribute *const ap_bus_attrs[] = { &bus_attr_ap_domain, &bus_attr_config_time, &bus_attr_poll_thread, - NULL + &bus_attr_poll_timeout, + NULL, }; /** @@ -895,9 +928,10 @@ ap_config_timeout(unsigned long ptr) */ static inline void ap_schedule_poll_timer(void) { - if (timer_pending(&ap_poll_timer)) + if (hrtimer_is_queued(&ap_poll_timer)) return; - mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME); + hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), + HRTIMER_MODE_ABS); } /** @@ -1115,13 +1149,14 @@ EXPORT_SYMBOL(ap_cancel_message); /** * ap_poll_timeout(): AP receive polling for finished AP requests. - * @unused: Unused variable. + * @unused: Unused pointer. * - * Schedules the AP tasklet. + * Schedules the AP tasklet using a high resolution timer. */ -static void ap_poll_timeout(unsigned long unused) +static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused) { tasklet_schedule(&ap_tasklet); + return HRTIMER_NORESTART; } /** @@ -1344,6 +1379,14 @@ int __init ap_module_init(void) ap_config_timer.expires = jiffies + ap_config_time * HZ; add_timer(&ap_config_timer); + /* Setup the high resultion poll timer. + * If we are running under z/VM adjust polling to z/VM polling rate. + */ + if (MACHINE_IS_VM) + poll_timeout = 1500000; + hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + ap_poll_timer.function = ap_poll_timeout; + /* Start the low priority AP bus poll thread. */ if (ap_thread_flag) { rc = ap_poll_thread_start(); @@ -1355,7 +1398,7 @@ int __init ap_module_init(void) out_work: del_timer_sync(&ap_config_timer); - del_timer_sync(&ap_poll_timer); + hrtimer_cancel(&ap_poll_timer); destroy_workqueue(ap_work_queue); out_root: s390_root_dev_unregister(ap_root_device); @@ -1386,7 +1429,7 @@ void ap_module_exit(void) ap_reset_domain(); ap_poll_thread_stop(); del_timer_sync(&ap_config_timer); - del_timer_sync(&ap_poll_timer); + hrtimer_cancel(&ap_poll_timer); destroy_workqueue(ap_work_queue); tasklet_kill(&ap_tasklet); s390_root_dev_unregister(ap_root_device); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index c1e1200c43f..446378b308f 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -92,6 +92,8 @@ struct ap_queue_status { #define AP_DEVICE_TYPE_PCIXCC 5 #define AP_DEVICE_TYPE_CEX2A 6 #define AP_DEVICE_TYPE_CEX2C 7 +#define AP_DEVICE_TYPE_CEX2A2 8 +#define AP_DEVICE_TYPE_CEX2C2 9 /* * AP reset flag states diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 4d36e805a23..8a4964f3584 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1068,10 +1068,8 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer, #define LBUFSIZE 1200UL lbuf = kmalloc(LBUFSIZE, GFP_KERNEL); - if (!lbuf) { - PRINTK("kmalloc failed!\n"); + if (!lbuf) return 0; - } local_count = min(LBUFSIZE - 1, count); if (copy_from_user(lbuf, buffer, local_count) != 0) { @@ -1081,23 +1079,15 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer, lbuf[local_count] = '\0'; ptr = strstr(lbuf, "Online devices"); - if (!ptr) { - PRINTK("Unable to parse data (missing \"Online devices\")\n"); + if (!ptr) goto out; - } ptr = strstr(ptr, "\n"); - if (!ptr) { - PRINTK("Unable to parse data (missing newline " - "after \"Online devices\")\n"); + if (!ptr) goto out; - } ptr++; - if (strstr(ptr, "Waiting work element counts") == NULL) { - PRINTK("Unable to parse data (missing " - "\"Waiting work element counts\")\n"); + if (strstr(ptr, "Waiting work element counts") == NULL) goto out; - } for (j = 0; j < 64 && *ptr; ptr++) { /* @@ -1197,16 +1187,12 @@ int __init zcrypt_api_init(void) /* Register the request sprayer. */ rc = misc_register(&zcrypt_misc_device); - if (rc < 0) { - PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n", - zcrypt_misc_device.minor, rc); + if (rc < 0) goto out; - } /* Set up the proc file system */ zcrypt_entry = create_proc_entry("driver/z90crypt", 0644, NULL); if (!zcrypt_entry) { - PRINTK("Couldn't create z90crypt proc entry\n"); rc = -ENOMEM; goto out_misc; } diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 5c6e222b2ac..1d1ec74dadb 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -30,34 +30,6 @@ #ifndef _ZCRYPT_API_H_ #define _ZCRYPT_API_H_ -/** - * Macro definitions - * - * PDEBUG debugs in the form "zcrypt: function_name -> message" - * - * PRINTK is like PDEBUG, except that it is always enabled - * PRINTKN is like PRINTK, except that it does not include the function name - * PRINTKW is like PRINTK, except that it uses KERN_WARNING - * PRINTKC is like PRINTK, except that it uses KERN_CRIT - */ -#define DEV_NAME "zcrypt" - -#define PRINTK(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args) -#define PRINTKN(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": " fmt, ## args) -#define PRINTKW(fmt, args...) \ - printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __func__ , ## args) -#define PRINTKC(fmt, args...) \ - printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __func__ , ## args) - -#ifdef ZCRYPT_DEBUG -#define PDEBUG(fmt, args...) \ - printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __func__ , ## args) -#else -#define PDEBUG(fmt, args...) do {} while (0) -#endif - #include "ap_bus.h" #include <asm/zcrypt.h> diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 08657f604b8..54f4cbc3be9 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -49,6 +49,7 @@ static struct ap_device_id zcrypt_cex2a_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) }, { /* end of list */ }, }; @@ -242,9 +243,6 @@ static int convert_response(struct zcrypt_device *zdev, return convert_type80(zdev, reply, outputdata, outputdatalength); default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h index 3e27fe77d20..03ba27f05f9 100644 --- a/drivers/s390/crypto/zcrypt_error.h +++ b/drivers/s390/crypto/zcrypt_error.h @@ -92,10 +92,6 @@ static inline int convert_error(struct zcrypt_device *zdev, { struct error_hdr *ehdr = reply->message; - PRINTK("Hardware error : Type %02x Message Header: %08x%08x\n", - ehdr->type, *(unsigned int *) reply->message, - *(unsigned int *) (reply->message + 4)); - switch (ehdr->reply_code) { case REP82_ERROR_OPERAND_INVALID: case REP82_ERROR_OPERAND_SIZE: @@ -123,8 +119,6 @@ static inline int convert_error(struct zcrypt_device *zdev, zdev->online = 0; return -EAGAIN; default: - PRINTKW("unknown type %02x reply code = %d\n", - ehdr->type, ehdr->reply_code); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 6e93b475178..12da4815ba8 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -226,9 +226,6 @@ static int convert_response(struct zcrypt_device *zdev, return convert_type84(zdev, reply, outputdata, outputdatalength); default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index 17ea56ce1c1..779952cb19f 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -361,26 +361,18 @@ static int convert_type86(struct zcrypt_device *zdev, service_rc = le16_to_cpu(msg->cprb.ccp_rtcode); if (unlikely(service_rc != 0)) { service_rs = le16_to_cpu(msg->cprb.ccp_rscode); - if (service_rc == 8 && service_rs == 66) { - PDEBUG("Bad block format on PCICC\n"); + if (service_rc == 8 && service_rs == 66) return -EINVAL; - } - if (service_rc == 8 && service_rs == 65) { - PDEBUG("Probably an even modulus on PCICC\n"); + if (service_rc == 8 && service_rs == 65) return -EINVAL; - } if (service_rc == 8 && service_rs == 770) { - PDEBUG("Invalid key length on PCICC\n"); zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD; return -EAGAIN; } if (service_rc == 8 && service_rs == 783) { - PDEBUG("Extended bitlengths not enabled on PCICC\n"); zdev->max_mod_size = PCICC_MAX_MOD_SIZE_OLD; return -EAGAIN; } - PRINTK("Unknown service rc/rs (PCICC): %d/%d\n", - service_rc, service_rs); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } @@ -434,9 +426,6 @@ static int convert_response(struct zcrypt_device *zdev, outputdata, outputdatalength); /* no break, incorrect cprb version is an unknown response */ default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 0bc9b3188e6..d8ad36f8154 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -72,6 +72,7 @@ struct response_type { static struct ap_device_id zcrypt_pcixcc_ids[] = { { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) }, { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) }, + { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) }, { /* end of list */ }, }; @@ -289,38 +290,19 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, ap_msg->length = sizeof(struct type6_hdr) + CEIL4(xcRB->request_control_blk_length) + xcRB->request_data_length; - if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) { - PRINTK("Combined message is too large (%ld/%d/%d).\n", - sizeof(struct type6_hdr), - xcRB->request_control_blk_length, - xcRB->request_data_length); + if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) return -EFAULT; - } - if (CEIL4(xcRB->reply_control_blk_length) > - PCIXCC_MAX_XCRB_REPLY_SIZE) { - PDEBUG("Reply CPRB length is too large (%d).\n", - xcRB->request_control_blk_length); + if (CEIL4(xcRB->reply_control_blk_length) > PCIXCC_MAX_XCRB_REPLY_SIZE) return -EFAULT; - } - if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) { - PDEBUG("Reply data block length is too large (%d).\n", - xcRB->reply_data_length); + if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) return -EFAULT; - } replylen = CEIL4(xcRB->reply_control_blk_length) + CEIL4(xcRB->reply_data_length) + sizeof(struct type86_fmt2_msg); if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) { - PDEBUG("Reply CPRB + data block > PCIXCC_MAX_XCRB_RESPONSE_SIZE" - " (%d/%d/%d).\n", - sizeof(struct type86_fmt2_msg), - xcRB->reply_control_blk_length, - xcRB->reply_data_length); xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE - (sizeof(struct type86_fmt2_msg) + CEIL4(xcRB->reply_data_length)); - PDEBUG("Capping Reply CPRB length at %d\n", - xcRB->reply_control_blk_length); } /* prepare type6 header */ @@ -339,11 +321,8 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, xcRB->request_control_blk_length)) return -EFAULT; if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) > - xcRB->request_control_blk_length) { - PDEBUG("cprb_len too large (%d/%d)\n", msg->cprbx.cprb_len, - xcRB->request_control_blk_length); + xcRB->request_control_blk_length) return -EFAULT; - } function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); @@ -471,29 +450,18 @@ static int convert_type86_ica(struct zcrypt_device *zdev, service_rc = msg->cprbx.ccp_rtcode; if (unlikely(service_rc != 0)) { service_rs = msg->cprbx.ccp_rscode; - if (service_rc == 8 && service_rs == 66) { - PDEBUG("Bad block format on PCIXCC/CEX2C\n"); + if (service_rc == 8 && service_rs == 66) return -EINVAL; - } - if (service_rc == 8 && service_rs == 65) { - PDEBUG("Probably an even modulus on PCIXCC/CEX2C\n"); + if (service_rc == 8 && service_rs == 65) return -EINVAL; - } - if (service_rc == 8 && service_rs == 770) { - PDEBUG("Invalid key length on PCIXCC/CEX2C\n"); + if (service_rc == 8 && service_rs == 770) return -EINVAL; - } if (service_rc == 8 && service_rs == 783) { - PDEBUG("Extended bitlengths not enabled on PCIXCC/CEX2C\n"); zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; return -EAGAIN; } - if (service_rc == 12 && service_rs == 769) { - PDEBUG("Invalid key on PCIXCC/CEX2C\n"); + if (service_rc == 12 && service_rs == 769) return -EINVAL; - } - PRINTK("Unknown service rc/rs (PCIXCC/CEX2C): %d/%d\n", - service_rc, service_rs); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } @@ -569,11 +537,8 @@ static int convert_type86_rng(struct zcrypt_device *zdev, } __attribute__((packed)) *msg = reply->message; char *data = reply->message; - if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0) { - PDEBUG("RNG response error on PCIXCC/CEX2C rc=%hu/rs=%hu\n", - rc, rs); + if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0) return -EINVAL; - } memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2); return msg->fmt2.count2; } @@ -598,9 +563,6 @@ static int convert_response_ica(struct zcrypt_device *zdev, outputdata, outputdatalength); /* no break, incorrect cprb version is an unknown response */ default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } @@ -627,9 +589,6 @@ static int convert_response_xcrb(struct zcrypt_device *zdev, return convert_type86_xcrb(zdev, reply, xcRB); /* no break, incorrect cprb version is an unknown response */ default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); xcRB->status = 0x0008044DL; /* HDD_InvalidParm */ zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ @@ -653,9 +612,6 @@ static int convert_response_rng(struct zcrypt_device *zdev, return convert_type86_rng(zdev, reply, data); /* no break, incorrect cprb version is an unknown response */ default: /* Unknown response type, this should NEVER EVER happen */ - PRINTK("Unrecognized Message Header: %08x%08x\n", - *(unsigned int *) reply->message, - *(unsigned int *) (reply->message+4)); zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } @@ -700,10 +656,7 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev, memcpy(msg->message, reply->message, length); break; default: - PRINTK("Invalid internal response type: %i\n", - resp_type->type); - memcpy(msg->message, &error_reply, - sizeof error_reply); + memcpy(msg->message, &error_reply, sizeof error_reply); } } else memcpy(msg->message, reply->message, sizeof error_reply); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 04a1d7bf678..c644669a75c 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -703,7 +703,8 @@ claw_irq_handler(struct ccw_device *cdev, if (!cdev->dev.driver_data) { printk(KERN_WARNING "claw: unsolicited interrupt for device:" "%s received c-%02x d-%02x\n", - cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat); + cdev->dev.bus_id, irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); #ifdef FUNCTRACE printk(KERN_INFO "claw: %s() " "exit on line %d\n",__func__,__LINE__); @@ -732,22 +733,23 @@ claw_irq_handler(struct ccw_device *cdev, #ifdef IOTRACE printk(KERN_INFO "%s: interrupt for device: %04x " "received c-%02x d-%02x state-%02x\n", - dev->name, p_ch->devno, irb->scsw.cstat, - irb->scsw.dstat, p_ch->claw_state); + dev->name, p_ch->devno, irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat, p_ch->claw_state); #endif /* Copy interruption response block. */ memcpy(p_ch->irb, irb, sizeof(struct irb)); /* Check for good subchannel return code, otherwise error message */ - if (irb->scsw.cstat && !(irb->scsw.cstat & SCHN_STAT_PCI)) { + if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) { printk(KERN_INFO "%s: subchannel check for device: %04x -" " Sch Stat %02x Dev Stat %02x CPA - %04x\n", dev->name, p_ch->devno, - irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa); + irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, + irb->scsw.cmd.cpa); #ifdef IOTRACE dumpit((char *)irb,sizeof(struct irb)); - dumpit((char *)(unsigned long)irb->scsw.cpa, + dumpit((char *)(unsigned long)irb->scsw.cmd.cpa, sizeof(struct ccw1)); #endif #ifdef FUNCTRACE @@ -759,22 +761,24 @@ claw_irq_handler(struct ccw_device *cdev, } /* Check the reason-code of a unit check */ - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) ccw_check_unit_check(p_ch, irb->ecw[0]); - } /* State machine to bring the connection up, down and to restart */ - p_ch->last_dstat = irb->scsw.dstat; + p_ch->last_dstat = irb->scsw.cmd.dstat; switch (p_ch->claw_state) { case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */ #ifdef DEBUGMSG printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name); #endif - if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.stctl == - (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + if (!((p_ch->irb->scsw.cmd.stctl & + SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == + SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | + SCSW_STCTL_STATUS_PEND)))) { #ifdef FUNCTRACE printk(KERN_INFO "%s:%s Exit on line %d\n", dev->name,__func__,__LINE__); @@ -798,10 +802,13 @@ claw_irq_handler(struct ccw_device *cdev, printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n", dev->name); #endif - if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.stctl == - (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + if (!((p_ch->irb->scsw.cmd.stctl & + SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == + SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | + SCSW_STCTL_STATUS_PEND)))) { #ifdef FUNCTRACE printk(KERN_INFO "%s:%s Exit on line %d\n", dev->name,__func__,__LINE__); @@ -828,8 +835,8 @@ claw_irq_handler(struct ccw_device *cdev, "interrupt for device:" "%s received c-%02x d-%02x\n", cdev->dev.bus_id, - irb->scsw.cstat, - irb->scsw.dstat); + irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); return; } #ifdef DEBUGMSG @@ -844,7 +851,7 @@ claw_irq_handler(struct ccw_device *cdev, return; case CLAW_START_READ: CLAW_DBF_TEXT(4,trace,"ReadIRQ"); - if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { clear_bit(0, (void *)&p_ch->IO_active); if ((p_ch->irb->ecw[0] & 0x41) == 0x41 || (p_ch->irb->ecw[0] & 0x40) == 0x40 || @@ -863,8 +870,8 @@ claw_irq_handler(struct ccw_device *cdev, CLAW_DBF_TEXT(4,trace,"notrdy"); return; } - if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) && - (p_ch->irb->scsw.dstat==0)) { + if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) && + (p_ch->irb->scsw.cmd.dstat == 0)) { if (test_and_set_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a) == 0) { tasklet_schedule(&p_ch->tasklet); @@ -879,10 +886,13 @@ claw_irq_handler(struct ccw_device *cdev, CLAW_DBF_TEXT(4,trace,"PCI_read"); return; } - if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.stctl == - (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + if (!((p_ch->irb->scsw.cmd.stctl & + SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == + SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | + SCSW_STCTL_STATUS_PEND)))) { #ifdef FUNCTRACE printk(KERN_INFO "%s:%s Exit on line %d\n", dev->name,__func__,__LINE__); @@ -911,7 +921,7 @@ claw_irq_handler(struct ccw_device *cdev, CLAW_DBF_TEXT(4,trace,"RdIRQXit"); return; case CLAW_START_WRITE: - if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { printk(KERN_INFO "%s: Unit Check Occured in " "write channel\n",dev->name); clear_bit(0, (void *)&p_ch->IO_active); @@ -934,16 +944,19 @@ claw_irq_handler(struct ccw_device *cdev, CLAW_DBF_TEXT(4,trace,"rstrtwrt"); return; } - if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) { + if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) { clear_bit(0, (void *)&p_ch->IO_active); printk(KERN_INFO "%s: Unit Exception " "Occured in write channel\n", dev->name); } - if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (p_ch->irb->scsw.stctl == - (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) { + if (!((p_ch->irb->scsw.cmd.stctl & + SCSW_STCTL_SEC_STATUS) || + (p_ch->irb->scsw.cmd.stctl == + SCSW_STCTL_STATUS_PEND) || + (p_ch->irb->scsw.cmd.stctl == + (SCSW_STCTL_ALERT_STATUS | + SCSW_STCTL_STATUS_PEND)))) { #ifdef FUNCTRACE printk(KERN_INFO "%s:%s Exit on line %d\n", dev->name,__func__,__LINE__); diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index 2a106f3a076..7e6bd387f4d 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -257,9 +257,9 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; - if (ch->irb->scsw.count != 0) + if (ch->irb->scsw.cmd.count != 0) ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.count); + dev->name, ch->irb->scsw.cmd.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { priv->stats.tx_packets++; @@ -353,7 +353,7 @@ static void chx_rx(fsm_instance *fi, int event, void *arg) struct channel *ch = arg; struct net_device *dev = ch->netdev; struct ctcm_priv *priv = dev->priv; - int len = ch->max_bufsize - ch->irb->scsw.count; + int len = ch->max_bufsize - ch->irb->scsw.cmd.count; struct sk_buff *skb = ch->trans_skb; __u16 block_len = *((__u16 *)skb->data); int check_len; @@ -1234,9 +1234,9 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; - if (ch->irb->scsw.count != 0) + if (ch->irb->scsw.cmd.count != 0) ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.count); + dev->name, ch->irb->scsw.cmd.count); fsm_deltimer(&ch->timer); while ((skb = skb_dequeue(&ch->io_queue))) { priv->stats.tx_packets++; @@ -1394,7 +1394,7 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg) struct sk_buff *skb = ch->trans_skb; struct sk_buff *new_skb; unsigned long saveflags = 0; /* avoids compiler warning */ - int len = ch->max_bufsize - ch->irb->scsw.count; + int len = ch->max_bufsize - ch->irb->scsw.cmd.count; if (do_debug_data) { CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n", diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index d52843da4f5..6b13c1c1beb 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1236,8 +1236,8 @@ static void ctcm_irq_handler(struct ccw_device *cdev, /* Check for unsolicited interrupts. */ if (cgdev == NULL) { ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n", - cdev->dev.bus_id, irb->scsw.cstat, - irb->scsw.dstat); + cdev->dev.bus_id, irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); return; } @@ -1266,40 +1266,40 @@ static void ctcm_irq_handler(struct ccw_device *cdev, "received c-%02x d-%02x\n", dev->name, ch->id, - irb->scsw.cstat, - irb->scsw.dstat); + irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); /* Copy interruption response block. */ memcpy(ch->irb, irb, sizeof(struct irb)); /* Check for good subchannel return code, otherwise error message */ - if (irb->scsw.cstat) { + if (irb->scsw.cmd.cstat) { fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch); ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n", - dev->name, ch->id, irb->scsw.cstat, - irb->scsw.dstat); + dev->name, ch->id, irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); return; } /* Check the reason-code of a unit check */ - if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { ccw_unit_check(ch, irb->ecw[0]); return; } - if (irb->scsw.dstat & DEV_STAT_BUSY) { - if (irb->scsw.dstat & DEV_STAT_ATTENTION) + if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch); else fsm_event(ch->fsm, CTC_EVENT_BUSY, ch); return; } - if (irb->scsw.dstat & DEV_STAT_ATTENTION) { + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { fsm_event(ch->fsm, CTC_EVENT_ATTN, ch); return; } - if ((irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (irb->scsw.stctl == + if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || + (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || + (irb->scsw.cmd.stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch); else diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c index 8e7697305a4..f4a32375c03 100644 --- a/drivers/s390/net/cu3088.c +++ b/drivers/s390/net/cu3088.c @@ -36,7 +36,6 @@ const char *cu3088_type[] = { "CTC/A", "ESCON channel", "FICON channel", - "P390 LCS card", "OSA LCS card", "CLAW channel device", "unknown channel type", @@ -49,7 +48,6 @@ static struct ccw_device_id cu3088_ids[] = { { CCW_DEVICE(0x3088, 0x08), .driver_info = channel_type_parallel }, { CCW_DEVICE(0x3088, 0x1f), .driver_info = channel_type_escon }, { CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon }, - { CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 }, { CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 }, { CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw }, { /* end of list */ } diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h index 1753661f702..d8558a7105a 100644 --- a/drivers/s390/net/cu3088.h +++ b/drivers/s390/net/cu3088.h @@ -17,9 +17,6 @@ enum channel_types { /* Device is a FICON channel */ channel_type_ficon, - /* Device is a P390 LCS card */ - channel_type_p390, - /* Device is a OSA2 card */ channel_type_osa2, diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index dd22f4b3703..6de28385b35 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1327,8 +1327,8 @@ lcs_get_problem(struct ccw_device *cdev, struct irb *irb) char *sense; sense = (char *) irb->ecw; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | @@ -1388,11 +1388,13 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) else channel = &card->write; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id); - LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat); - LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl); + LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat, + irb->scsw.cmd.dstat); + LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl, + irb->scsw.cmd.actl); /* Check for channel and device errors presented */ rc = lcs_get_problem(cdev, irb); @@ -1410,11 +1412,11 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } /* How far in the ccw chain have we processed? */ if ((channel->state != LCS_CH_STATE_INIT) && - (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) { - index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa) + (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC)) { + index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa) - channel->ccws; - if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) || - (irb->scsw.cstat & SCHN_STAT_PCI)) + if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) || + (irb->scsw.cmd.cstat & SCHN_STAT_PCI)) /* Bloody io subsystem tells us lies about cpa... */ index = (index - 1) & (LCS_NUM_BUFFS - 1); while (channel->io_idx != index) { @@ -1425,25 +1427,24 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } } - if ((irb->scsw.dstat & DEV_STAT_DEV_END) || - (irb->scsw.dstat & DEV_STAT_CHN_END) || - (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) + if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) || + (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) || + (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)) /* Mark channel as stopped. */ channel->state = LCS_CH_STATE_STOPPED; - else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED) + else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) /* CCW execution stopped on a suspend bit. */ channel->state = LCS_CH_STATE_SUSPENDED; - if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) { - if (irb->scsw.cc != 0) { + if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) { + if (irb->scsw.cmd.cc != 0) { ccw_device_halt(channel->ccwdev, (addr_t) channel); return; } /* The channel has been stopped by halt_IO. */ channel->state = LCS_CH_STATE_HALTED; } - if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { + if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) channel->state = LCS_CH_STATE_CLEARED; - } /* Do the rest in the tasklet. */ tasklet_schedule(&channel->irq_tasklet); } @@ -1761,7 +1762,7 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd) netif_carrier_off(card->dev); break; default: - PRINT_INFO("UNRECOGNIZED LGW COMMAND\n"); + LCS_DBF_TEXT(5, trace, "noLGWcmd"); break; } } else @@ -2042,13 +2043,12 @@ lcs_probe_device(struct ccwgroup_device *ccwgdev) LCS_DBF_TEXT(2, setup, "add_dev"); card = lcs_alloc_card(); if (!card) { - PRINT_ERR("Allocation of lcs card failed\n"); + LCS_DBF_TEXT_(2, setup, " rc%d", -ENOMEM); put_device(&ccwgdev->dev); return -ENOMEM; } ret = sysfs_create_group(&ccwgdev->dev.kobj, &lcs_attr_group); if (ret) { - PRINT_ERR("Creating attributes failed"); lcs_free_card(card); put_device(&ccwgdev->dev); return ret; @@ -2140,7 +2140,6 @@ lcs_new_device(struct ccwgroup_device *ccwgdev) default: LCS_DBF_TEXT(3, setup, "errinit"); PRINT_ERR("LCS: Initialization failed\n"); - PRINT_ERR("LCS: No device found!\n"); goto out; } if (!dev) @@ -2269,7 +2268,6 @@ lcs_remove_device(struct ccwgroup_device *ccwgdev) if (!card) return; - PRINT_INFO("Removing lcs group device ....\n"); LCS_DBF_TEXT(3, setup, "remdev"); LCS_DBF_HEX(3, setup, &card, sizeof(void*)); if (ccwgdev->state == CCWGROUP_ONLINE) { diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index e4ba6a0372a..9242b5acc66 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -625,9 +625,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, offset += header->next; header->next -= NETIUCV_HDRLEN; if (skb_tailroom(pskb) < header->next) { - PRINT_WARN("%s: Illegal next field in iucv header: " - "%d > %d\n", - dev->name, header->next, skb_tailroom(pskb)); IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n", header->next, skb_tailroom(pskb)); return; @@ -636,8 +633,6 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, skb_reset_mac_header(pskb); skb = dev_alloc_skb(pskb->len); if (!skb) { - PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n", - dev->name); IUCV_DBF_TEXT(data, 2, "Out of memory in netiucv_unpack_skb\n"); privptr->stats.rx_dropped++; @@ -674,7 +669,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg) if (!conn->netdev) { iucv_message_reject(conn->path, msg); - PRINT_WARN("Received data for unlinked connection\n"); IUCV_DBF_TEXT(data, 2, "Received data for unlinked connection\n"); return; @@ -682,8 +676,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg) if (msg->length > conn->max_buffsize) { iucv_message_reject(conn->path, msg); privptr->stats.rx_dropped++; - PRINT_WARN("msglen %d > max_buffsize %d\n", - msg->length, conn->max_buffsize); IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n", msg->length, conn->max_buffsize); return; @@ -695,7 +687,6 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg) msg->length, NULL); if (rc || msg->length < 5) { privptr->stats.rx_errors++; - PRINT_WARN("iucv_receive returned %08x\n", rc); IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc); return; } @@ -778,7 +769,6 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, CONN_STATE_IDLE); if (privptr) privptr->stats.tx_errors += txpackets; - PRINT_WARN("iucv_send returned %08x\n", rc); IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc); } else { if (privptr) { @@ -806,8 +796,6 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg) path->flags = 0; rc = iucv_path_accept(path, &netiucv_handler, NULL, conn); if (rc) { - PRINT_WARN("%s: IUCV accept failed with error %d\n", - netdev->name, rc); IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc); return; } @@ -873,7 +861,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_newstate(fi, CONN_STATE_STARTWAIT); - PRINT_DEBUG("%s('%s'): connecting ...\n", + IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n", conn->netdev->name, conn->userid); /* @@ -968,8 +956,8 @@ static void conn_action_inval(fsm_instance *fi, int event, void *arg) struct iucv_connection *conn = arg; struct net_device *netdev = conn->netdev; - PRINT_WARN("%s: Cannot connect without username\n", netdev->name); - IUCV_DBF_TEXT(data, 2, "conn_action_inval called\n"); + IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n", + netdev->name, conn->userid); } static const fsm_node conn_fsm[] = { @@ -1077,9 +1065,6 @@ dev_action_connup(fsm_instance *fi, int event, void *arg) "connection is up and running\n"); break; case DEV_STATE_STOPWAIT: - PRINT_INFO( - "%s: got connection UP event during shutdown!\n", - dev->name); IUCV_DBF_TEXT(data, 2, "dev_action_connup: in DEV_STATE_STOPWAIT\n"); break; @@ -1174,8 +1159,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, nskb = alloc_skb(skb->len + NETIUCV_HDRLEN + NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA); if (!nskb) { - PRINT_WARN("%s: Could not allocate tx_skb\n", - conn->netdev->name); IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n"); rc = -ENOMEM; return rc; @@ -1223,7 +1206,6 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, skb_pull(skb, NETIUCV_HDRLEN); skb_trim(skb, skb->len - NETIUCV_HDRLEN); } - PRINT_WARN("iucv_send returned %08x\n", rc); IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc); } else { if (copied) @@ -1293,14 +1275,11 @@ static int netiucv_tx(struct sk_buff *skb, struct net_device *dev) * Some sanity checks ... */ if (skb == NULL) { - PRINT_WARN("%s: NULL sk_buff passed\n", dev->name); IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n"); privptr->stats.tx_dropped++; return 0; } if (skb_headroom(skb) < NETIUCV_HDRLEN) { - PRINT_WARN("%s: Got sk_buff with head room < %ld bytes\n", - dev->name, NETIUCV_HDRLEN); IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n"); dev_kfree_skb(skb); @@ -1393,7 +1372,6 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr, IUCV_DBF_TEXT(trace, 3, __func__); if (count > 9) { - PRINT_WARN("netiucv: username too long (%d)!\n", (int) count); IUCV_DBF_TEXT_(setup, 2, "%d is length of username\n", (int) count); return -EINVAL; @@ -1409,7 +1387,6 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr, /* trailing lf, grr */ break; } - PRINT_WARN("netiucv: Invalid char %c in username!\n", *p); IUCV_DBF_TEXT_(setup, 2, "username: invalid character %c\n", *p); return -EINVAL; @@ -1421,18 +1398,15 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr, if (memcmp(username, priv->conn->userid, 9) && (ndev->flags & (IFF_UP | IFF_RUNNING))) { /* username changed while the interface is active. */ - PRINT_WARN("netiucv: device %s active, connected to %s\n", - dev->bus_id, priv->conn->userid); - PRINT_WARN("netiucv: user cannot be updated\n"); IUCV_DBF_TEXT(setup, 2, "user_write: device active\n"); - return -EBUSY; + return -EPERM; } read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) { read_unlock_bh(&iucv_connection_rwlock); - PRINT_WARN("netiucv: Connection to %s already " - "exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "user_write: Connection " + "to %s already exists\n", username); return -EEXIST; } } @@ -1466,13 +1440,10 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, bs1 = simple_strtoul(buf, &e, 0); if (e && (!isspace(*e))) { - PRINT_WARN("netiucv: Invalid character in buffer!\n"); IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e); return -EINVAL; } if (bs1 > NETIUCV_BUFSIZE_MAX) { - PRINT_WARN("netiucv: Given buffer size %d too large.\n", - bs1); IUCV_DBF_TEXT_(setup, 2, "buffer_write: buffer size %d too large\n", bs1); @@ -1480,16 +1451,12 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, } if ((ndev->flags & IFF_RUNNING) && (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) { - PRINT_WARN("netiucv: Given buffer size %d too small.\n", - bs1); IUCV_DBF_TEXT_(setup, 2, "buffer_write: buffer size %d too small\n", bs1); return -EINVAL; } if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) { - PRINT_WARN("netiucv: Given buffer size %d too small.\n", - bs1); IUCV_DBF_TEXT_(setup, 2, "buffer_write: buffer size %d too small\n", bs1); @@ -1963,7 +1930,6 @@ static ssize_t conn_write(struct device_driver *drv, IUCV_DBF_TEXT(trace, 3, __func__); if (count>9) { - PRINT_WARN("netiucv: username too long (%d)!\n", (int)count); IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); return -EINVAL; } @@ -1976,7 +1942,6 @@ static ssize_t conn_write(struct device_driver *drv, if (*p == '\n') /* trailing lf, grr */ break; - PRINT_WARN("netiucv: Invalid character in username!\n"); IUCV_DBF_TEXT_(setup, 2, "conn_write: invalid character %c\n", *p); return -EINVAL; @@ -1989,8 +1954,8 @@ static ssize_t conn_write(struct device_driver *drv, list_for_each_entry(cp, &iucv_connection_list, list) { if (!strncmp(username, cp->userid, 9)) { read_unlock_bh(&iucv_connection_rwlock); - PRINT_WARN("netiucv: Connection to %s already " - "exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection " + "to %s already exists\n", username); return -EEXIST; } } @@ -1998,9 +1963,6 @@ static ssize_t conn_write(struct device_driver *drv, dev = netiucv_init_netdevice(username); if (!dev) { - PRINT_WARN("netiucv: Could not allocate network device " - "structure for user '%s'\n", - netiucv_printname(username)); IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); return -ENODEV; } @@ -2020,15 +1982,12 @@ static ssize_t conn_write(struct device_driver *drv, if (rc) goto out_unreg; - PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username)); return count; out_unreg: netiucv_unregister_device(priv->dev); out_free_ndev: - PRINT_WARN("netiucv: Could not register '%s'\n", dev->name); - IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n"); netiucv_free_netdevice(dev); return rc; } @@ -2073,14 +2032,13 @@ static ssize_t remove_write (struct device_driver *drv, PRINT_WARN("netiucv: %s cannot be removed\n", ndev->name); IUCV_DBF_TEXT(data, 2, "remove_write: still active\n"); - return -EBUSY; + return -EPERM; } unregister_netdev(ndev); netiucv_unregister_device(dev); return count; } read_unlock_bh(&iucv_connection_rwlock); - PRINT_WARN("netiucv: net device %s unknown\n", name); IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); return -EINVAL; } @@ -2148,7 +2106,6 @@ static int __init netiucv_init(void) netiucv_driver.groups = netiucv_drv_attr_groups; rc = driver_register(&netiucv_driver); if (rc) { - PRINT_ERR("NETIUCV: failed to register driver.\n"); IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc); goto out_iucv; } diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9a71dae223e..0ac54dc638c 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -420,7 +420,7 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card, QETH_DBF_TEXT(TRACE, 3, "urla"); break; default: - PRINT_WARN("Received data is IPA " + QETH_DBF_MESSAGE(2, "Received data is IPA " "but not a reply!\n"); break; } @@ -735,8 +735,8 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb) char *sense; sense = (char *) irb->ecw; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK | SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK | @@ -823,8 +823,8 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, if (__qeth_check_irb_error(cdev, intparm, irb)) return; - cstat = irb->scsw.cstat; - dstat = irb->scsw.dstat; + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; card = CARD_FROM_CDEV(cdev); if (!card) @@ -842,10 +842,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm, } atomic_set(&channel->irq_pending, 0); - if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC)) + if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC)) channel->state = CH_STATE_STOPPED; - if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC)) + if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC)) channel->state = CH_STATE_HALTED; /*let's wake up immediately on data channel*/ @@ -4092,7 +4092,6 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) rc = qeth_determine_card_type(card); if (rc) { - PRINT_WARN("%s: not a valid card type\n", __func__); QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc); goto err_card; } diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 999552c83bb..06deaee50f6 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -944,15 +944,8 @@ static int qeth_l3_deregister_addr_entry(struct qeth_card *card, else rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP, addr->del_flags); - if (rc) { + if (rc) QETH_DBF_TEXT(TRACE, 2, "failed"); - /* TODO: re-activate this warning as soon as we have a - * clean mirco code - qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf); - PRINT_WARN("Could not deregister IP address %s (rc=%x)\n", - buf, rc); - */ - } return rc; } diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 8735a415a11..164e090c262 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c @@ -156,11 +156,8 @@ static int __init smsg_init(void) if (rc != 0) goto out; rc = iucv_register(&smsg_handler, 1); - if (rc) { - printk(KERN_ERR "SMSGIUCV: failed to register to iucv"); - rc = -EIO; /* better errno ? */ + if (rc) goto out_driver; - } smsg_path = iucv_path_alloc(255, 0, GFP_KERNEL); if (!smsg_path) { rc = -ENOMEM; @@ -168,11 +165,8 @@ static int __init smsg_init(void) } rc = iucv_path_connect(smsg_path, &smsg_handler, "*MSG ", NULL, NULL, NULL); - if (rc) { - printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG"); - rc = -EIO; /* better errno ? */ + if (rc) goto out_free; - } cpcmd("SET SMSG IUCV", NULL, 0, NULL); return 0; diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 5bfbe765983..834e9ee7e93 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -2,10 +2,10 @@ * drivers/s390/s390mach.c * S/390 machine check handler * - * S390 version - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright IBM Corp. 2000,2008 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com) + * Cornelia Huck <cornelia.huck@de.ibm.com> */ #include <linux/init.h> @@ -18,10 +18,6 @@ #include <asm/etr.h> #include <asm/lowcore.h> #include <asm/cio.h> -#include "cio/cio.h" -#include "cio/chsc.h" -#include "cio/css.h" -#include "cio/chp.h" #include "s390mach.h" static struct semaphore m_sem; @@ -36,13 +32,40 @@ s390_handle_damage(char *msg) for(;;); } +static crw_handler_t crw_handlers[NR_RSCS]; + +/** + * s390_register_crw_handler() - register a channel report word handler + * @rsc: reporting source code to handle + * @handler: handler to be registered + * + * Returns %0 on success and a negative error value otherwise. + */ +int s390_register_crw_handler(int rsc, crw_handler_t handler) +{ + if ((rsc < 0) || (rsc >= NR_RSCS)) + return -EINVAL; + if (!cmpxchg(&crw_handlers[rsc], NULL, handler)) + return 0; + return -EBUSY; +} + +/** + * s390_unregister_crw_handler() - unregister a channel report word handler + * @rsc: reporting source code to handle + */ +void s390_unregister_crw_handler(int rsc) +{ + if ((rsc < 0) || (rsc >= NR_RSCS)) + return; + xchg(&crw_handlers[rsc], NULL); + synchronize_sched(); +} + /* * Retrieve CRWs and call function to handle event. - * - * Note : we currently process CRWs for io and chsc subchannels only */ -static int -s390_collect_crw_info(void *param) +static int s390_collect_crw_info(void *param) { struct crw crw[2]; int ccode; @@ -84,57 +107,24 @@ repeat: crw[chain].rsid); /* Check for overflows. */ if (crw[chain].oflw) { + int i; + pr_debug("%s: crw overflow detected!\n", __func__); - css_schedule_eval_all(); + for (i = 0; i < NR_RSCS; i++) { + if (crw_handlers[i]) + crw_handlers[i](NULL, NULL, 1); + } chain = 0; continue; } - switch (crw[chain].rsc) { - case CRW_RSC_SCH: - if (crw[0].chn && !chain) - break; - pr_debug("source is subchannel %04X\n", crw[0].rsid); - css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0); - break; - case CRW_RSC_MONITOR: - pr_debug("source is monitoring facility\n"); - break; - case CRW_RSC_CPATH: - pr_debug("source is channel path %02X\n", crw[0].rsid); - /* - * Check for solicited machine checks. These are - * created by reset channel path and need not be - * reported to the common I/O layer. - */ - if (crw[chain].slct) { - pr_debug("solicited machine check for " - "channel path %02X\n", crw[0].rsid); - break; - } - switch (crw[0].erc) { - case CRW_ERC_IPARM: /* Path has come. */ - chp_process_crw(crw[0].rsid, 1); - break; - case CRW_ERC_PERRI: /* Path has gone. */ - case CRW_ERC_PERRN: - chp_process_crw(crw[0].rsid, 0); - break; - default: - pr_debug("Don't know how to handle erc=%x\n", - crw[0].erc); - } - break; - case CRW_RSC_CONFIG: - pr_debug("source is configuration-alert facility\n"); - break; - case CRW_RSC_CSS: - pr_debug("source is channel subsystem\n"); - chsc_process_crw(); - break; - default: - pr_debug("unknown source\n"); - break; + if (crw[0].chn && !chain) { + chain++; + continue; } + if (crw_handlers[crw[chain].rsc]) + crw_handlers[crw[chain].rsc](&crw[0], + chain ? &crw[1] : NULL, + 0); /* chain is always 0 or 1 here. */ chain = crw[chain].chn ? chain + 1 : 0; } @@ -468,6 +458,10 @@ s390_do_machine_check(struct pt_regs *regs) etr_sync_check(); if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH)) etr_switch_to_local(); + if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC)) + stp_sync_check(); + if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) + stp_island_check(); } if (mci->se) diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h index ca681f9b67f..d39f8b697d2 100644 --- a/drivers/s390/s390mach.h +++ b/drivers/s390/s390mach.h @@ -72,6 +72,13 @@ struct crw { __u32 rsid : 16; /* reporting-source ID */ } __attribute__ ((packed)); +typedef void (*crw_handler_t)(struct crw *, struct crw *, int); + +extern int s390_register_crw_handler(int rsc, crw_handler_t handler); +extern void s390_unregister_crw_handler(int rsc); + +#define NR_RSCS 16 + #define CRW_RSC_MONITOR 0x2 /* monitoring facility */ #define CRW_RSC_SCH 0x3 /* subchannel */ #define CRW_RSC_CPATH 0x4 /* channel path */ @@ -105,6 +112,9 @@ static inline int stcrw(struct crw *pcrw ) #define ED_ETR_SYNC 12 /* External damage ETR sync check */ #define ED_ETR_SWITCH 13 /* External damage ETR switch to local */ +#define ED_STP_SYNC 7 /* External damage STP sync check */ +#define ED_STP_ISLAND 6 /* External damage STP island check */ + struct pt_regs; void s390_handle_mcck(void); diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index ea0edd1b2e7..fe694f0ee19 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -182,8 +182,9 @@ static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize); static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp); -static ssize_t sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp); +static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, + const char __user *buf, size_t count, int blocking, + int read_only, Sg_request **o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking); static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, @@ -204,7 +205,6 @@ static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_allow_access(unsigned char opcode, char dev_type); static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len); static Sg_device *sg_get_dev(int dev); #ifdef CONFIG_SCSI_PROC_FS @@ -544,7 +544,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) return -EFAULT; blocking = !(filp->f_flags & O_NONBLOCK); if (old_hdr.reply_len < 0) - return sg_new_write(sfp, buf, count, blocking, 0, NULL); + return sg_new_write(sfp, filp, buf, count, blocking, 0, NULL); if (count < (SZ_SG_HEADER + 6)) return -EIO; /* The minimum scsi command length is 6 bytes. */ @@ -621,8 +621,9 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) } static ssize_t -sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp) +sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, + size_t count, int blocking, int read_only, + Sg_request **o_srp) { int k; Sg_request *srp; @@ -678,8 +679,7 @@ sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count, sg_remove_request(sfp, srp); return -EFAULT; } - if (read_only && - (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) { + if (read_only && !blk_verify_command(file, cmnd)) { sg_remove_request(sfp, srp); return -EPERM; } @@ -799,7 +799,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR)) return -EFAULT; result = - sg_new_write(sfp, p, SZ_SG_IO_HDR, + sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, blocking, read_only, &srp); if (result < 0) return result; @@ -1048,7 +1048,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (copy_from_user(&opcode, siocp->data, 1)) return -EFAULT; - if (!sg_allow_access(opcode, sdp->device->type)) + if (!blk_verify_command(filp, &opcode)) return -EPERM; } return sg_scsi_ioctl(filp, sdp->device->request_queue, NULL, p); @@ -2502,30 +2502,6 @@ sg_page_free(struct page *page, int size) __free_pages(page, order); } -#ifndef MAINTENANCE_IN_CMD -#define MAINTENANCE_IN_CMD 0xa3 -#endif - -static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE, - INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12, - READ_16, MODE_SENSE, MODE_SENSE_10, LOG_SENSE, REPORT_LUNS, - SERVICE_ACTION_IN, RECEIVE_DIAGNOSTIC, READ_LONG, MAINTENANCE_IN_CMD -}; - -static int -sg_allow_access(unsigned char opcode, char dev_type) -{ - int k; - - if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */ - return 1; - for (k = 0; k < sizeof (allow_ops); ++k) { - if (opcode == allow_ops[k]) - return 1; - } - return 0; -} - #ifdef CONFIG_SCSI_PROC_FS static int sg_idr_max_id(int id, void *p, void *data) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index c82df8bd4d8..27f5bfd1def 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -673,24 +673,20 @@ fail: static void get_sectorsize(struct scsi_cd *cd) { unsigned char cmd[10]; - unsigned char *buffer; + unsigned char buffer[8]; int the_result, retries = 3; int sector_size; struct request_queue *queue; - buffer = kmalloc(512, GFP_KERNEL | GFP_DMA); - if (!buffer) - goto Enomem; - do { cmd[0] = READ_CAPACITY; memset((void *) &cmd[1], 0, 9); - memset(buffer, 0, 8); + memset(buffer, 0, sizeof(buffer)); /* Do the command and wait.. */ the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE, - buffer, 8, NULL, SR_TIMEOUT, - MAX_RETRIES); + buffer, sizeof(buffer), NULL, + SR_TIMEOUT, MAX_RETRIES); retries--; @@ -745,14 +741,8 @@ static void get_sectorsize(struct scsi_cd *cd) queue = cd->device->request_queue; blk_queue_hardsect_size(queue, sector_size); -out: - kfree(buffer); - return; -Enomem: - cd->capacity = 0x1fffff; - cd->device->sector_size = 2048; /* A guess, just in case */ - goto out; + return; } static void get_capabilities(struct scsi_cd *cd) diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 0f86b0ff787..9678b3e98c6 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -117,7 +117,7 @@ int xenbus_watch_pathfmt(struct xenbus_device *dev, char *path; va_start(ap, pathfmt); - path = kvasprintf(GFP_KERNEL, pathfmt, ap); + path = kvasprintf(GFP_NOIO | __GFP_HIGH, pathfmt, ap); va_end(ap); if (!path) { diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 227d53b12a5..7f2f91c0e11 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -283,9 +283,9 @@ static char *join(const char *dir, const char *name) char *buffer; if (strlen(name) == 0) - buffer = kasprintf(GFP_KERNEL, "%s", dir); + buffer = kasprintf(GFP_NOIO | __GFP_HIGH, "%s", dir); else - buffer = kasprintf(GFP_KERNEL, "%s/%s", dir, name); + buffer = kasprintf(GFP_NOIO | __GFP_HIGH, "%s/%s", dir, name); return (!buffer) ? ERR_PTR(-ENOMEM) : buffer; } @@ -297,7 +297,7 @@ static char **split(char *strings, unsigned int len, unsigned int *num) *num = count_strings(strings, len); /* Transfer to one big alloc for easy freeing. */ - ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL); + ret = kmalloc(*num * sizeof(char *) + len, GFP_NOIO | __GFP_HIGH); if (!ret) { kfree(strings); return ERR_PTR(-ENOMEM); @@ -751,7 +751,7 @@ static int process_msg(void) } - msg = kmalloc(sizeof(*msg), GFP_KERNEL); + msg = kmalloc(sizeof(*msg), GFP_NOIO | __GFP_HIGH); if (msg == NULL) { err = -ENOMEM; goto out; @@ -763,7 +763,7 @@ static int process_msg(void) goto out; } - body = kmalloc(msg->hdr.len + 1, GFP_KERNEL); + body = kmalloc(msg->hdr.len + 1, GFP_NOIO | __GFP_HIGH); if (body == NULL) { kfree(msg); err = -ENOMEM; diff --git a/fs/Makefile b/fs/Makefile index 1e7a11bd4da..277b079dec9 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -19,6 +19,7 @@ else obj-y += no-block.o endif +obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_INOTIFY_USER) += inotify_user.o obj-$(CONFIG_EPOLL) += eventpoll.o diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c new file mode 100644 index 00000000000..63e2ee63058 --- /dev/null +++ b/fs/bio-integrity.c @@ -0,0 +1,719 @@ +/* + * bio-integrity.c - bio data integrity extensions + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen <martin.petersen@oracle.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + */ + +#include <linux/blkdev.h> +#include <linux/mempool.h> +#include <linux/bio.h> +#include <linux/workqueue.h> + +static struct kmem_cache *bio_integrity_slab __read_mostly; +static struct workqueue_struct *kintegrityd_wq; + +/** + * bio_integrity_alloc_bioset - Allocate integrity payload and attach it to bio + * @bio: bio to attach integrity metadata to + * @gfp_mask: Memory allocation mask + * @nr_vecs: Number of integrity metadata scatter-gather elements + * @bs: bio_set to allocate from + * + * Description: This function prepares a bio for attaching integrity + * metadata. nr_vecs specifies the maximum number of pages containing + * integrity metadata that can be attached. + */ +struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *bio, + gfp_t gfp_mask, + unsigned int nr_vecs, + struct bio_set *bs) +{ + struct bio_integrity_payload *bip; + struct bio_vec *iv; + unsigned long idx; + + BUG_ON(bio == NULL); + + bip = mempool_alloc(bs->bio_integrity_pool, gfp_mask); + if (unlikely(bip == NULL)) { + printk(KERN_ERR "%s: could not alloc bip\n", __func__); + return NULL; + } + + memset(bip, 0, sizeof(*bip)); + + iv = bvec_alloc_bs(gfp_mask, nr_vecs, &idx, bs); + if (unlikely(iv == NULL)) { + printk(KERN_ERR "%s: could not alloc bip_vec\n", __func__); + mempool_free(bip, bs->bio_integrity_pool); + return NULL; + } + + bip->bip_pool = idx; + bip->bip_vec = iv; + bip->bip_bio = bio; + bio->bi_integrity = bip; + + return bip; +} +EXPORT_SYMBOL(bio_integrity_alloc_bioset); + +/** + * bio_integrity_alloc - Allocate integrity payload and attach it to bio + * @bio: bio to attach integrity metadata to + * @gfp_mask: Memory allocation mask + * @nr_vecs: Number of integrity metadata scatter-gather elements + * + * Description: This function prepares a bio for attaching integrity + * metadata. nr_vecs specifies the maximum number of pages containing + * integrity metadata that can be attached. + */ +struct bio_integrity_payload *bio_integrity_alloc(struct bio *bio, + gfp_t gfp_mask, + unsigned int nr_vecs) +{ + return bio_integrity_alloc_bioset(bio, gfp_mask, nr_vecs, fs_bio_set); +} +EXPORT_SYMBOL(bio_integrity_alloc); + +/** + * bio_integrity_free - Free bio integrity payload + * @bio: bio containing bip to be freed + * @bs: bio_set this bio was allocated from + * + * Description: Used to free the integrity portion of a bio. Usually + * called from bio_free(). + */ +void bio_integrity_free(struct bio *bio, struct bio_set *bs) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + + BUG_ON(bip == NULL); + + /* A cloned bio doesn't own the integrity metadata */ + if (!bio_flagged(bio, BIO_CLONED) && bip->bip_buf != NULL) + kfree(bip->bip_buf); + + mempool_free(bip->bip_vec, bs->bvec_pools[bip->bip_pool]); + mempool_free(bip, bs->bio_integrity_pool); + + bio->bi_integrity = NULL; +} +EXPORT_SYMBOL(bio_integrity_free); + +/** + * bio_integrity_add_page - Attach integrity metadata + * @bio: bio to update + * @page: page containing integrity metadata + * @len: number of bytes of integrity metadata in page + * @offset: start offset within page + * + * Description: Attach a page containing integrity metadata to bio. + */ +int bio_integrity_add_page(struct bio *bio, struct page *page, + unsigned int len, unsigned int offset) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct bio_vec *iv; + + if (bip->bip_vcnt >= bvec_nr_vecs(bip->bip_pool)) { + printk(KERN_ERR "%s: bip_vec full\n", __func__); + return 0; + } + + iv = bip_vec_idx(bip, bip->bip_vcnt); + BUG_ON(iv == NULL); + BUG_ON(iv->bv_page != NULL); + + iv->bv_page = page; + iv->bv_len = len; + iv->bv_offset = offset; + bip->bip_vcnt++; + + return len; +} +EXPORT_SYMBOL(bio_integrity_add_page); + +/** + * bio_integrity_enabled - Check whether integrity can be passed + * @bio: bio to check + * + * Description: Determines whether bio_integrity_prep() can be called + * on this bio or not. bio data direction and target device must be + * set prior to calling. The functions honors the write_generate and + * read_verify flags in sysfs. + */ +int bio_integrity_enabled(struct bio *bio) +{ + /* Already protected? */ + if (bio_integrity(bio)) + return 0; + + return bdev_integrity_enabled(bio->bi_bdev, bio_data_dir(bio)); +} +EXPORT_SYMBOL(bio_integrity_enabled); + +/** + * bio_integrity_hw_sectors - Convert 512b sectors to hardware ditto + * @bi: blk_integrity profile for device + * @sectors: Number of 512 sectors to convert + * + * Description: The block layer calculates everything in 512 byte + * sectors but integrity metadata is done in terms of the hardware + * sector size of the storage device. Convert the block layer sectors + * to physical sectors. + */ +static inline unsigned int bio_integrity_hw_sectors(struct blk_integrity *bi, + unsigned int sectors) +{ + /* At this point there are only 512b or 4096b DIF/EPP devices */ + if (bi->sector_size == 4096) + return sectors >>= 3; + + return sectors; +} + +/** + * bio_integrity_tag_size - Retrieve integrity tag space + * @bio: bio to inspect + * + * Description: Returns the maximum number of tag bytes that can be + * attached to this bio. Filesystems can use this to determine how + * much metadata to attach to an I/O. + */ +unsigned int bio_integrity_tag_size(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + + BUG_ON(bio->bi_size == 0); + + return bi->tag_size * (bio->bi_size / bi->sector_size); +} +EXPORT_SYMBOL(bio_integrity_tag_size); + +int bio_integrity_tag(struct bio *bio, void *tag_buf, unsigned int len, int set) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip->bip_buf == NULL); + + if (bi->tag_size == 0) + return -1; + + nr_sectors = bio_integrity_hw_sectors(bi, + DIV_ROUND_UP(len, bi->tag_size)); + + if (nr_sectors * bi->tuple_size > bip->bip_size) { + printk(KERN_ERR "%s: tag too big for bio: %u > %u\n", + __func__, nr_sectors * bi->tuple_size, bip->bip_size); + return -1; + } + + if (set) + bi->set_tag_fn(bip->bip_buf, tag_buf, nr_sectors); + else + bi->get_tag_fn(bip->bip_buf, tag_buf, nr_sectors); + + return 0; +} + +/** + * bio_integrity_set_tag - Attach a tag buffer to a bio + * @bio: bio to attach buffer to + * @tag_buf: Pointer to a buffer containing tag data + * @len: Length of the included buffer + * + * Description: Use this function to tag a bio by leveraging the extra + * space provided by devices formatted with integrity protection. The + * size of the integrity buffer must be <= to the size reported by + * bio_integrity_tag_size(). + */ +int bio_integrity_set_tag(struct bio *bio, void *tag_buf, unsigned int len) +{ + BUG_ON(bio_data_dir(bio) != WRITE); + + return bio_integrity_tag(bio, tag_buf, len, 1); +} +EXPORT_SYMBOL(bio_integrity_set_tag); + +/** + * bio_integrity_get_tag - Retrieve a tag buffer from a bio + * @bio: bio to retrieve buffer from + * @tag_buf: Pointer to a buffer for the tag data + * @len: Length of the target buffer + * + * Description: Use this function to retrieve the tag buffer from a + * completed I/O. The size of the integrity buffer must be <= to the + * size reported by bio_integrity_tag_size(). + */ +int bio_integrity_get_tag(struct bio *bio, void *tag_buf, unsigned int len) +{ + BUG_ON(bio_data_dir(bio) != READ); + + return bio_integrity_tag(bio, tag_buf, len, 0); +} +EXPORT_SYMBOL(bio_integrity_get_tag); + +/** + * bio_integrity_generate - Generate integrity metadata for a bio + * @bio: bio to generate integrity metadata for + * + * Description: Generates integrity metadata for a bio by calling the + * block device's generation callback function. The bio must have a + * bip attached with enough room to accommodate the generated + * integrity metadata. + */ +static void bio_integrity_generate(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + struct blk_integrity_exchg bix; + struct bio_vec *bv; + sector_t sector = bio->bi_sector; + unsigned int i, sectors, total; + void *prot_buf = bio->bi_integrity->bip_buf; + + total = 0; + bix.disk_name = bio->bi_bdev->bd_disk->disk_name; + bix.sector_size = bi->sector_size; + + bio_for_each_segment(bv, bio, i) { + void *kaddr = kmap_atomic(bv->bv_page, KM_USER0); + bix.data_buf = kaddr + bv->bv_offset; + bix.data_size = bv->bv_len; + bix.prot_buf = prot_buf; + bix.sector = sector; + + bi->generate_fn(&bix); + + sectors = bv->bv_len / bi->sector_size; + sector += sectors; + prot_buf += sectors * bi->tuple_size; + total += sectors * bi->tuple_size; + BUG_ON(total > bio->bi_integrity->bip_size); + + kunmap_atomic(kaddr, KM_USER0); + } +} + +/** + * bio_integrity_prep - Prepare bio for integrity I/O + * @bio: bio to prepare + * + * Description: Allocates a buffer for integrity metadata, maps the + * pages and attaches them to a bio. The bio must have data + * direction, target device and start sector set priot to calling. In + * the WRITE case, integrity metadata will be generated using the + * block device's integrity function. In the READ case, the buffer + * will be prepared for DMA and a suitable end_io handler set up. + */ +int bio_integrity_prep(struct bio *bio) +{ + struct bio_integrity_payload *bip; + struct blk_integrity *bi; + struct request_queue *q; + void *buf; + unsigned long start, end; + unsigned int len, nr_pages; + unsigned int bytes, offset, i; + unsigned int sectors; + + bi = bdev_get_integrity(bio->bi_bdev); + q = bdev_get_queue(bio->bi_bdev); + BUG_ON(bi == NULL); + BUG_ON(bio_integrity(bio)); + + sectors = bio_integrity_hw_sectors(bi, bio_sectors(bio)); + + /* Allocate kernel buffer for protection data */ + len = sectors * blk_integrity_tuple_size(bi); + buf = kmalloc(len, GFP_NOIO | __GFP_NOFAIL | q->bounce_gfp); + if (unlikely(buf == NULL)) { + printk(KERN_ERR "could not allocate integrity buffer\n"); + return -EIO; + } + + end = (((unsigned long) buf) + len + PAGE_SIZE - 1) >> PAGE_SHIFT; + start = ((unsigned long) buf) >> PAGE_SHIFT; + nr_pages = end - start; + + /* Allocate bio integrity payload and integrity vectors */ + bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages); + if (unlikely(bip == NULL)) { + printk(KERN_ERR "could not allocate data integrity bioset\n"); + kfree(buf); + return -EIO; + } + + bip->bip_buf = buf; + bip->bip_size = len; + bip->bip_sector = bio->bi_sector; + + /* Map it */ + offset = offset_in_page(buf); + for (i = 0 ; i < nr_pages ; i++) { + int ret; + bytes = PAGE_SIZE - offset; + + if (len <= 0) + break; + + if (bytes > len) + bytes = len; + + ret = bio_integrity_add_page(bio, virt_to_page(buf), + bytes, offset); + + if (ret == 0) + return 0; + + if (ret < bytes) + break; + + buf += bytes; + len -= bytes; + offset = 0; + } + + /* Install custom I/O completion handler if read verify is enabled */ + if (bio_data_dir(bio) == READ) { + bip->bip_end_io = bio->bi_end_io; + bio->bi_end_io = bio_integrity_endio; + } + + /* Auto-generate integrity metadata if this is a write */ + if (bio_data_dir(bio) == WRITE) + bio_integrity_generate(bio); + + return 0; +} +EXPORT_SYMBOL(bio_integrity_prep); + +/** + * bio_integrity_verify - Verify integrity metadata for a bio + * @bio: bio to verify + * + * Description: This function is called to verify the integrity of a + * bio. The data in the bio io_vec is compared to the integrity + * metadata returned by the HBA. + */ +static int bio_integrity_verify(struct bio *bio) +{ + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + struct blk_integrity_exchg bix; + struct bio_vec *bv; + sector_t sector = bio->bi_integrity->bip_sector; + unsigned int i, sectors, total, ret; + void *prot_buf = bio->bi_integrity->bip_buf; + + ret = total = 0; + bix.disk_name = bio->bi_bdev->bd_disk->disk_name; + bix.sector_size = bi->sector_size; + + bio_for_each_segment(bv, bio, i) { + void *kaddr = kmap_atomic(bv->bv_page, KM_USER0); + bix.data_buf = kaddr + bv->bv_offset; + bix.data_size = bv->bv_len; + bix.prot_buf = prot_buf; + bix.sector = sector; + + ret = bi->verify_fn(&bix); + + if (ret) { + kunmap_atomic(kaddr, KM_USER0); + break; + } + + sectors = bv->bv_len / bi->sector_size; + sector += sectors; + prot_buf += sectors * bi->tuple_size; + total += sectors * bi->tuple_size; + BUG_ON(total > bio->bi_integrity->bip_size); + + kunmap_atomic(kaddr, KM_USER0); + } + + return ret; +} + +/** + * bio_integrity_verify_fn - Integrity I/O completion worker + * @work: Work struct stored in bio to be verified + * + * Description: This workqueue function is called to complete a READ + * request. The function verifies the transferred integrity metadata + * and then calls the original bio end_io function. + */ +static void bio_integrity_verify_fn(struct work_struct *work) +{ + struct bio_integrity_payload *bip = + container_of(work, struct bio_integrity_payload, bip_work); + struct bio *bio = bip->bip_bio; + int error = bip->bip_error; + + if (bio_integrity_verify(bio)) { + clear_bit(BIO_UPTODATE, &bio->bi_flags); + error = -EIO; + } + + /* Restore original bio completion handler */ + bio->bi_end_io = bip->bip_end_io; + + if (bio->bi_end_io) + bio->bi_end_io(bio, error); +} + +/** + * bio_integrity_endio - Integrity I/O completion function + * @bio: Protected bio + * @error: Pointer to errno + * + * Description: Completion for integrity I/O + * + * Normally I/O completion is done in interrupt context. However, + * verifying I/O integrity is a time-consuming task which must be run + * in process context. This function postpones completion + * accordingly. + */ +void bio_integrity_endio(struct bio *bio, int error) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + + BUG_ON(bip->bip_bio != bio); + + bip->bip_error = error; + INIT_WORK(&bip->bip_work, bio_integrity_verify_fn); + queue_work(kintegrityd_wq, &bip->bip_work); +} +EXPORT_SYMBOL(bio_integrity_endio); + +/** + * bio_integrity_mark_head - Advance bip_vec skip bytes + * @bip: Integrity vector to advance + * @skip: Number of bytes to advance it + */ +void bio_integrity_mark_head(struct bio_integrity_payload *bip, + unsigned int skip) +{ + struct bio_vec *iv; + unsigned int i; + + bip_for_each_vec(iv, bip, i) { + if (skip == 0) { + bip->bip_idx = i; + return; + } else if (skip >= iv->bv_len) { + skip -= iv->bv_len; + } else { /* skip < iv->bv_len) */ + iv->bv_offset += skip; + iv->bv_len -= skip; + bip->bip_idx = i; + return; + } + } +} + +/** + * bio_integrity_mark_tail - Truncate bip_vec to be len bytes long + * @bip: Integrity vector to truncate + * @len: New length of integrity vector + */ +void bio_integrity_mark_tail(struct bio_integrity_payload *bip, + unsigned int len) +{ + struct bio_vec *iv; + unsigned int i; + + bip_for_each_vec(iv, bip, i) { + if (len == 0) { + bip->bip_vcnt = i; + return; + } else if (len >= iv->bv_len) { + len -= iv->bv_len; + } else { /* len < iv->bv_len) */ + iv->bv_len = len; + len = 0; + } + } +} + +/** + * bio_integrity_advance - Advance integrity vector + * @bio: bio whose integrity vector to update + * @bytes_done: number of data bytes that have been completed + * + * Description: This function calculates how many integrity bytes the + * number of completed data bytes correspond to and advances the + * integrity vector accordingly. + */ +void bio_integrity_advance(struct bio *bio, unsigned int bytes_done) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip == NULL); + BUG_ON(bi == NULL); + + nr_sectors = bio_integrity_hw_sectors(bi, bytes_done >> 9); + bio_integrity_mark_head(bip, nr_sectors * bi->tuple_size); +} +EXPORT_SYMBOL(bio_integrity_advance); + +/** + * bio_integrity_trim - Trim integrity vector + * @bio: bio whose integrity vector to update + * @offset: offset to first data sector + * @sectors: number of data sectors + * + * Description: Used to trim the integrity vector in a cloned bio. + * The ivec will be advanced corresponding to 'offset' data sectors + * and the length will be truncated corresponding to 'len' data + * sectors. + */ +void bio_integrity_trim(struct bio *bio, unsigned int offset, + unsigned int sectors) +{ + struct bio_integrity_payload *bip = bio->bi_integrity; + struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev); + unsigned int nr_sectors; + + BUG_ON(bip == NULL); + BUG_ON(bi == NULL); + BUG_ON(!bio_flagged(bio, BIO_CLONED)); + + nr_sectors = bio_integrity_hw_sectors(bi, sectors); + bip->bip_sector = bip->bip_sector + offset; + bio_integrity_mark_head(bip, offset * bi->tuple_size); + bio_integrity_mark_tail(bip, sectors * bi->tuple_size); +} +EXPORT_SYMBOL(bio_integrity_trim); + +/** + * bio_integrity_split - Split integrity metadata + * @bio: Protected bio + * @bp: Resulting bio_pair + * @sectors: Offset + * + * Description: Splits an integrity page into a bio_pair. + */ +void bio_integrity_split(struct bio *bio, struct bio_pair *bp, int sectors) +{ + struct blk_integrity *bi; + struct bio_integrity_payload *bip = bio->bi_integrity; + unsigned int nr_sectors; + + if (bio_integrity(bio) == 0) + return; + + bi = bdev_get_integrity(bio->bi_bdev); + BUG_ON(bi == NULL); + BUG_ON(bip->bip_vcnt != 1); + + nr_sectors = bio_integrity_hw_sectors(bi, sectors); + + bp->bio1.bi_integrity = &bp->bip1; + bp->bio2.bi_integrity = &bp->bip2; + + bp->iv1 = bip->bip_vec[0]; + bp->iv2 = bip->bip_vec[0]; + + bp->bip1.bip_vec = &bp->iv1; + bp->bip2.bip_vec = &bp->iv2; + + bp->iv1.bv_len = sectors * bi->tuple_size; + bp->iv2.bv_offset += sectors * bi->tuple_size; + bp->iv2.bv_len -= sectors * bi->tuple_size; + + bp->bip1.bip_sector = bio->bi_integrity->bip_sector; + bp->bip2.bip_sector = bio->bi_integrity->bip_sector + nr_sectors; + + bp->bip1.bip_vcnt = bp->bip2.bip_vcnt = 1; + bp->bip1.bip_idx = bp->bip2.bip_idx = 0; +} +EXPORT_SYMBOL(bio_integrity_split); + +/** + * bio_integrity_clone - Callback for cloning bios with integrity metadata + * @bio: New bio + * @bio_src: Original bio + * @bs: bio_set to allocate bip from + * + * Description: Called to allocate a bip when cloning a bio + */ +int bio_integrity_clone(struct bio *bio, struct bio *bio_src, + struct bio_set *bs) +{ + struct bio_integrity_payload *bip_src = bio_src->bi_integrity; + struct bio_integrity_payload *bip; + + BUG_ON(bip_src == NULL); + + bip = bio_integrity_alloc_bioset(bio, GFP_NOIO, bip_src->bip_vcnt, bs); + + if (bip == NULL) + return -EIO; + + memcpy(bip->bip_vec, bip_src->bip_vec, + bip_src->bip_vcnt * sizeof(struct bio_vec)); + + bip->bip_sector = bip_src->bip_sector; + bip->bip_vcnt = bip_src->bip_vcnt; + bip->bip_idx = bip_src->bip_idx; + + return 0; +} +EXPORT_SYMBOL(bio_integrity_clone); + +int bioset_integrity_create(struct bio_set *bs, int pool_size) +{ + bs->bio_integrity_pool = mempool_create_slab_pool(pool_size, + bio_integrity_slab); + if (!bs->bio_integrity_pool) + return -1; + + return 0; +} +EXPORT_SYMBOL(bioset_integrity_create); + +void bioset_integrity_free(struct bio_set *bs) +{ + if (bs->bio_integrity_pool) + mempool_destroy(bs->bio_integrity_pool); +} +EXPORT_SYMBOL(bioset_integrity_free); + +void __init bio_integrity_init_slab(void) +{ + bio_integrity_slab = KMEM_CACHE(bio_integrity_payload, + SLAB_HWCACHE_ALIGN|SLAB_PANIC); +} +EXPORT_SYMBOL(bio_integrity_init_slab); + +static int __init integrity_init(void) +{ + kintegrityd_wq = create_workqueue("kintegrityd"); + + if (!kintegrityd_wq) + panic("Failed to create kintegrityd\n"); + + return 0; +} +subsys_initcall(integrity_init); @@ -28,25 +28,10 @@ #include <linux/blktrace_api.h> #include <scsi/sg.h> /* for struct sg_iovec */ -#define BIO_POOL_SIZE 2 - static struct kmem_cache *bio_slab __read_mostly; -#define BIOVEC_NR_POOLS 6 - -/* - * a small number of entries is fine, not going to be performance critical. - * basically we just need to survive - */ -#define BIO_SPLIT_ENTRIES 2 mempool_t *bio_split_pool __read_mostly; -struct biovec_slab { - int nr_vecs; - char *name; - struct kmem_cache *slab; -}; - /* * if you change this list, also change bvec_alloc or things will * break badly! cannot be bigger than what you can fit into an @@ -60,23 +45,17 @@ static struct biovec_slab bvec_slabs[BIOVEC_NR_POOLS] __read_mostly = { #undef BV /* - * bio_set is used to allow other portions of the IO system to - * allocate their own private memory pools for bio and iovec structures. - * These memory pools in turn all allocate from the bio_slab - * and the bvec_slabs[]. - */ -struct bio_set { - mempool_t *bio_pool; - mempool_t *bvec_pools[BIOVEC_NR_POOLS]; -}; - -/* * fs_bio_set is the bio_set containing bio and iovec memory pools used by * IO code that does not need private memory pools. */ -static struct bio_set *fs_bio_set; +struct bio_set *fs_bio_set; + +unsigned int bvec_nr_vecs(unsigned short idx) +{ + return bvec_slabs[idx].nr_vecs; +} -static inline struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) +struct bio_vec *bvec_alloc_bs(gfp_t gfp_mask, int nr, unsigned long *idx, struct bio_set *bs) { struct bio_vec *bvl; @@ -117,6 +96,9 @@ void bio_free(struct bio *bio, struct bio_set *bio_set) mempool_free(bio->bi_io_vec, bio_set->bvec_pools[pool_idx]); } + if (bio_integrity(bio)) + bio_integrity_free(bio, bio_set); + mempool_free(bio, bio_set->bio_pool); } @@ -275,9 +257,19 @@ struct bio *bio_clone(struct bio *bio, gfp_t gfp_mask) { struct bio *b = bio_alloc_bioset(gfp_mask, bio->bi_max_vecs, fs_bio_set); - if (b) { - b->bi_destructor = bio_fs_destructor; - __bio_clone(b, bio); + if (!b) + return NULL; + + b->bi_destructor = bio_fs_destructor; + __bio_clone(b, bio); + + if (bio_integrity(bio)) { + int ret; + + ret = bio_integrity_clone(b, bio, fs_bio_set); + + if (ret < 0) + return NULL; } return b; @@ -333,10 +325,19 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page if (page == prev->bv_page && offset == prev->bv_offset + prev->bv_len) { prev->bv_len += len; - if (q->merge_bvec_fn && - q->merge_bvec_fn(q, bio, prev) < len) { - prev->bv_len -= len; - return 0; + + if (q->merge_bvec_fn) { + struct bvec_merge_data bvm = { + .bi_bdev = bio->bi_bdev, + .bi_sector = bio->bi_sector, + .bi_size = bio->bi_size, + .bi_rw = bio->bi_rw, + }; + + if (q->merge_bvec_fn(q, &bvm, prev) < len) { + prev->bv_len -= len; + return 0; + } } goto done; @@ -377,11 +378,18 @@ static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page * queue to get further control */ if (q->merge_bvec_fn) { + struct bvec_merge_data bvm = { + .bi_bdev = bio->bi_bdev, + .bi_sector = bio->bi_sector, + .bi_size = bio->bi_size, + .bi_rw = bio->bi_rw, + }; + /* * merge_bvec_fn() returns number of bytes it can accept * at this offset */ - if (q->merge_bvec_fn(q, bio, bvec) < len) { + if (q->merge_bvec_fn(q, &bvm, bvec) < len) { bvec->bv_page = NULL; bvec->bv_len = 0; bvec->bv_offset = 0; @@ -1249,6 +1257,9 @@ struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors) bp->bio1.bi_private = bi; bp->bio2.bi_private = pool; + if (bio_integrity(bi)) + bio_integrity_split(bi, bp, first_sectors); + return bp; } @@ -1290,6 +1301,7 @@ void bioset_free(struct bio_set *bs) if (bs->bio_pool) mempool_destroy(bs->bio_pool); + bioset_integrity_free(bs); biovec_free_pools(bs); kfree(bs); @@ -1306,6 +1318,9 @@ struct bio_set *bioset_create(int bio_pool_size, int bvec_pool_size) if (!bs->bio_pool) goto bad; + if (bioset_integrity_create(bs, bio_pool_size)) + goto bad; + if (!biovec_create_pools(bs, bvec_pool_size)) return bs; @@ -1332,6 +1347,7 @@ static int __init init_bio(void) { bio_slab = KMEM_CACHE(bio, SLAB_HWCACHE_ALIGN|SLAB_PANIC); + bio_integrity_init_slab(); biovec_init_slabs(); fs_bio_set = bioset_create(BIO_POOL_SIZE, 2); diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 9590b902430..78f613cb9c7 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -45,6 +45,7 @@ const struct file_operations ramfs_file_operations = { .mmap = generic_file_mmap, .fsync = simple_sync_file, .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, .llseek = generic_file_llseek, }; diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 0989bc2c2f6..52312ec93ff 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -43,6 +43,7 @@ const struct file_operations ramfs_file_operations = { .aio_write = generic_file_aio_write, .fsync = simple_sync_file, .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, .llseek = generic_file_llseek, }; diff --git a/fs/splice.c b/fs/splice.c index aa5f6f60b30..399442179d8 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -379,13 +379,22 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, lock_page(page); /* - * page was truncated, stop here. if this isn't the - * first page, we'll just complete what we already - * added + * Page was truncated, or invalidated by the + * filesystem. Redo the find/create, but this time the + * page is kept locked, so there's no chance of another + * race with truncate/invalidate. */ if (!page->mapping) { unlock_page(page); - break; + page = find_or_create_page(mapping, index, + mapping_gfp_mask(mapping)); + + if (!page) { + error = -ENOMEM; + break; + } + page_cache_release(pages[page_nr]); + pages[page_nr] = page; } /* * page was already under io and is now done, great diff --git a/include/asm-s390/Kbuild b/include/asm-s390/Kbuild index 13c9805349f..09f312501eb 100644 --- a/include/asm-s390/Kbuild +++ b/include/asm-s390/Kbuild @@ -8,6 +8,9 @@ header-y += ucontext.h header-y += vtoc.h header-y += zcrypt.h header-y += kvm.h +header-y += schid.h +header-y += chsc.h unifdef-y += cmb.h unifdef-y += debug.h +unifdef-y += chpid.h diff --git a/include/asm-s390/airq.h b/include/asm-s390/airq.h index 41d028cb52a..1ac80d6b058 100644 --- a/include/asm-s390/airq.h +++ b/include/asm-s390/airq.h @@ -13,7 +13,7 @@ typedef void (*adapter_int_handler_t)(void *, void *); -void *s390_register_adapter_interrupt(adapter_int_handler_t, void *); -void s390_unregister_adapter_interrupt(void *); +void *s390_register_adapter_interrupt(adapter_int_handler_t, void *, u8); +void s390_unregister_adapter_interrupt(void *, u8); #endif /* _ASM_S390_AIRQ_H */ diff --git a/include/asm-s390/ccwdev.h b/include/asm-s390/ccwdev.h index 066aa70518c..ba007d8df94 100644 --- a/include/asm-s390/ccwdev.h +++ b/include/asm-s390/ccwdev.h @@ -12,6 +12,7 @@ #include <linux/device.h> #include <linux/mod_devicetable.h> +#include <asm/fcx.h> /* structs from asm/cio.h */ struct irb; @@ -157,6 +158,17 @@ extern int ccw_device_start_timeout_key(struct ccw_device *, struct ccw1 *, extern int ccw_device_resume(struct ccw_device *); extern int ccw_device_halt(struct ccw_device *, unsigned long); extern int ccw_device_clear(struct ccw_device *, unsigned long); +int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, + unsigned long intparm, u8 lpm, u8 key); +int ccw_device_tm_start_key(struct ccw_device *, struct tcw *, + unsigned long, u8, u8); +int ccw_device_tm_start_timeout_key(struct ccw_device *, struct tcw *, + unsigned long, u8, u8, int); +int ccw_device_tm_start(struct ccw_device *, struct tcw *, + unsigned long, u8); +int ccw_device_tm_start_timeout(struct ccw_device *, struct tcw *, + unsigned long, u8, int); +int ccw_device_tm_intrg(struct ccw_device *cdev); extern int ccw_device_set_online(struct ccw_device *cdev); extern int ccw_device_set_offline(struct ccw_device *cdev); diff --git a/include/asm-s390/chpid.h b/include/asm-s390/chpid.h index b203336fd89..606844d0a5c 100644 --- a/include/asm-s390/chpid.h +++ b/include/asm-s390/chpid.h @@ -10,7 +10,6 @@ #include <linux/string.h> #include <asm/types.h> -#include <asm/cio.h> #define __MAX_CHPID 255 @@ -41,6 +40,9 @@ static inline void chp_id_next(struct chp_id *chpid) } } +#ifdef __KERNEL__ +#include <asm/cio.h> + static inline int chp_id_is_valid(struct chp_id *chpid) { return (chpid->cssid <= __MAX_CSSID); @@ -49,5 +51,6 @@ static inline int chp_id_is_valid(struct chp_id *chpid) #define chp_id_for_each(c) \ for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) +#endif /* __KERNEL */ #endif /* _ASM_S390_CHPID_H */ diff --git a/include/asm-s390/chsc.h b/include/asm-s390/chsc.h new file mode 100644 index 00000000000..d38d0cf62d4 --- /dev/null +++ b/include/asm-s390/chsc.h @@ -0,0 +1,127 @@ +/* + * ioctl interface for /dev/chsc + * + * Copyright 2008 IBM Corp. + * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> + */ + +#ifndef _ASM_CHSC_H +#define _ASM_CHSC_H + +#include <asm/chpid.h> +#include <asm/schid.h> + +struct chsc_async_header { + __u16 length; + __u16 code; + __u32 cmd_dependend; + __u32 key : 4; + __u32 : 28; + struct subchannel_id sid; +} __attribute__ ((packed)); + +struct chsc_async_area { + struct chsc_async_header header; + __u8 data[PAGE_SIZE - 16 /* size of chsc_async_header */]; +} __attribute__ ((packed)); + + +struct chsc_response_struct { + __u16 length; + __u16 code; + __u32 parms; + __u8 data[PAGE_SIZE - 8]; +} __attribute__ ((packed)); + +struct chsc_chp_cd { + struct chp_id chpid; + int m; + int fmt; + struct chsc_response_struct cpcb; +}; + +struct chsc_cu_cd { + __u16 cun; + __u8 cssid; + int m; + int fmt; + struct chsc_response_struct cucb; +}; + +struct chsc_sch_cud { + struct subchannel_id schid; + int fmt; + struct chsc_response_struct scub; +}; + +struct conf_id { + int m; + __u8 cssid; + __u8 ssid; +}; + +struct chsc_conf_info { + struct conf_id id; + int fmt; + struct chsc_response_struct scid; +}; + +struct ccl_parm_chpid { + int m; + struct chp_id chp; +}; + +struct ccl_parm_cssids { + __u8 f_cssid; + __u8 l_cssid; +}; + +struct chsc_comp_list { + struct { + enum { + CCL_CU_ON_CHP = 1, + CCL_CHP_TYPE_CAP = 2, + CCL_CSS_IMG = 4, + CCL_CSS_IMG_CONF_CHAR = 5, + CCL_IOP_CHP = 6, + } ctype; + int fmt; + struct ccl_parm_chpid chpid; + struct ccl_parm_cssids cssids; + } req; + struct chsc_response_struct sccl; +}; + +struct chsc_dcal { + struct { + enum { + DCAL_CSS_IID_PN = 4, + } atype; + __u32 list_parm[2]; + int fmt; + } req; + struct chsc_response_struct sdcal; +}; + +struct chsc_cpd_info { + struct chp_id chpid; + int m; + int fmt; + int rfmt; + int c; + struct chsc_response_struct chpdb; +}; + +#define CHSC_IOCTL_MAGIC 'c' + +#define CHSC_START _IOWR(CHSC_IOCTL_MAGIC, 0x81, struct chsc_async_area) +#define CHSC_INFO_CHANNEL_PATH _IOWR(CHSC_IOCTL_MAGIC, 0x82, \ + struct chsc_chp_cd) +#define CHSC_INFO_CU _IOWR(CHSC_IOCTL_MAGIC, 0x83, struct chsc_cu_cd) +#define CHSC_INFO_SCH_CU _IOWR(CHSC_IOCTL_MAGIC, 0x84, struct chsc_sch_cud) +#define CHSC_INFO_CI _IOWR(CHSC_IOCTL_MAGIC, 0x85, struct chsc_conf_info) +#define CHSC_INFO_CCL _IOWR(CHSC_IOCTL_MAGIC, 0x86, struct chsc_comp_list) +#define CHSC_INFO_CPD _IOWR(CHSC_IOCTL_MAGIC, 0x87, struct chsc_cpd_info) +#define CHSC_INFO_DCAL _IOWR(CHSC_IOCTL_MAGIC, 0x88, struct chsc_dcal) + +#endif diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 0818ecd30ca..6dccb071aec 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -16,7 +16,7 @@ #define __MAX_CSSID 0 /** - * struct scsw - subchannel status word + * struct cmd_scsw - command-mode subchannel status word * @key: subchannel key * @sctl: suspend control * @eswf: esw format @@ -38,7 +38,7 @@ * @cstat: subchannel status * @count: residual count */ -struct scsw { +struct cmd_scsw { __u32 key : 4; __u32 sctl : 1; __u32 eswf : 1; @@ -61,6 +61,114 @@ struct scsw { __u32 count : 16; } __attribute__ ((packed)); +/** + * struct tm_scsw - transport-mode subchannel status word + * @key: subchannel key + * @eswf: esw format + * @cc: deferred condition code + * @fmt: format + * @x: IRB-format control + * @q: interrogate-complete + * @ectl: extended control + * @pno: path not operational + * @fctl: function control + * @actl: activity control + * @stctl: status control + * @tcw: TCW address + * @dstat: device status + * @cstat: subchannel status + * @fcxs: FCX status + * @schxs: subchannel-extended status + */ +struct tm_scsw { + u32 key:4; + u32 :1; + u32 eswf:1; + u32 cc:2; + u32 fmt:3; + u32 x:1; + u32 q:1; + u32 :1; + u32 ectl:1; + u32 pno:1; + u32 :1; + u32 fctl:3; + u32 actl:7; + u32 stctl:5; + u32 tcw; + u32 dstat:8; + u32 cstat:8; + u32 fcxs:8; + u32 schxs:8; +} __attribute__ ((packed)); + +/** + * union scsw - subchannel status word + * @cmd: command-mode SCSW + * @tm: transport-mode SCSW + */ +union scsw { + struct cmd_scsw cmd; + struct tm_scsw tm; +} __attribute__ ((packed)); + +int scsw_is_tm(union scsw *scsw); +u32 scsw_key(union scsw *scsw); +u32 scsw_eswf(union scsw *scsw); +u32 scsw_cc(union scsw *scsw); +u32 scsw_ectl(union scsw *scsw); +u32 scsw_pno(union scsw *scsw); +u32 scsw_fctl(union scsw *scsw); +u32 scsw_actl(union scsw *scsw); +u32 scsw_stctl(union scsw *scsw); +u32 scsw_dstat(union scsw *scsw); +u32 scsw_cstat(union scsw *scsw); +int scsw_is_solicited(union scsw *scsw); +int scsw_is_valid_key(union scsw *scsw); +int scsw_is_valid_eswf(union scsw *scsw); +int scsw_is_valid_cc(union scsw *scsw); +int scsw_is_valid_ectl(union scsw *scsw); +int scsw_is_valid_pno(union scsw *scsw); +int scsw_is_valid_fctl(union scsw *scsw); +int scsw_is_valid_actl(union scsw *scsw); +int scsw_is_valid_stctl(union scsw *scsw); +int scsw_is_valid_dstat(union scsw *scsw); +int scsw_is_valid_cstat(union scsw *scsw); +int scsw_cmd_is_valid_key(union scsw *scsw); +int scsw_cmd_is_valid_sctl(union scsw *scsw); +int scsw_cmd_is_valid_eswf(union scsw *scsw); +int scsw_cmd_is_valid_cc(union scsw *scsw); +int scsw_cmd_is_valid_fmt(union scsw *scsw); +int scsw_cmd_is_valid_pfch(union scsw *scsw); +int scsw_cmd_is_valid_isic(union scsw *scsw); +int scsw_cmd_is_valid_alcc(union scsw *scsw); +int scsw_cmd_is_valid_ssi(union scsw *scsw); +int scsw_cmd_is_valid_zcc(union scsw *scsw); +int scsw_cmd_is_valid_ectl(union scsw *scsw); +int scsw_cmd_is_valid_pno(union scsw *scsw); +int scsw_cmd_is_valid_fctl(union scsw *scsw); +int scsw_cmd_is_valid_actl(union scsw *scsw); +int scsw_cmd_is_valid_stctl(union scsw *scsw); +int scsw_cmd_is_valid_dstat(union scsw *scsw); +int scsw_cmd_is_valid_cstat(union scsw *scsw); +int scsw_cmd_is_solicited(union scsw *scsw); +int scsw_tm_is_valid_key(union scsw *scsw); +int scsw_tm_is_valid_eswf(union scsw *scsw); +int scsw_tm_is_valid_cc(union scsw *scsw); +int scsw_tm_is_valid_fmt(union scsw *scsw); +int scsw_tm_is_valid_x(union scsw *scsw); +int scsw_tm_is_valid_q(union scsw *scsw); +int scsw_tm_is_valid_ectl(union scsw *scsw); +int scsw_tm_is_valid_pno(union scsw *scsw); +int scsw_tm_is_valid_fctl(union scsw *scsw); +int scsw_tm_is_valid_actl(union scsw *scsw); +int scsw_tm_is_valid_stctl(union scsw *scsw); +int scsw_tm_is_valid_dstat(union scsw *scsw); +int scsw_tm_is_valid_cstat(union scsw *scsw); +int scsw_tm_is_valid_fcxs(union scsw *scsw); +int scsw_tm_is_valid_schxs(union scsw *scsw); +int scsw_tm_is_solicited(union scsw *scsw); + #define SCSW_FCTL_CLEAR_FUNC 0x1 #define SCSW_FCTL_HALT_FUNC 0x2 #define SCSW_FCTL_START_FUNC 0x4 @@ -303,7 +411,7 @@ struct esw3 { * if applicable). */ struct irb { - struct scsw scsw; + union scsw scsw; union { struct esw0 esw0; struct esw1 esw1; diff --git a/include/asm-s390/elf.h b/include/asm-s390/elf.h index b3ac262c458..3cad5692381 100644 --- a/include/asm-s390/elf.h +++ b/include/asm-s390/elf.h @@ -113,6 +113,9 @@ typedef s390_fp_regs elf_fpregset_t; typedef s390_regs elf_gregset_t; +typedef s390_fp_regs compat_elf_fpregset_t; +typedef s390_compat_regs compat_elf_gregset_t; + #include <linux/sched.h> /* for task_struct */ #include <asm/system.h> /* for save_access_regs */ #include <asm/mmu_context.h> @@ -123,6 +126,10 @@ typedef s390_regs elf_gregset_t; #define elf_check_arch(x) \ (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \ && (x)->e_ident[EI_CLASS] == ELF_CLASS) +#define compat_elf_check_arch(x) \ + (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \ + && (x)->e_ident[EI_CLASS] == ELF_CLASS) +#define compat_start_thread start_thread31 /* For SVR4/S390 the function pointer to be registered with `atexit` is passed in R14. */ @@ -131,6 +138,7 @@ typedef s390_regs elf_gregset_t; _r->gprs[14] = 0; \ } while (0) +#define CORE_DUMP_USE_REGSET #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 @@ -140,44 +148,6 @@ typedef s390_regs elf_gregset_t; that it will "exec", and that there is sufficient room for the brk. */ #define ELF_ET_DYN_BASE (STACK_TOP / 3 * 2) -/* Wow, the "main" arch needs arch dependent functions too.. :) */ - -/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is - now struct_user_regs, they are different) */ - -static inline int dump_regs(struct pt_regs *ptregs, elf_gregset_t *regs) -{ - memcpy(®s->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs)); - save_access_regs(regs->acrs); - regs->orig_gpr2 = ptregs->orig_gpr2; - return 1; -} - -#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs(regs, &pr_reg); - -static inline int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) -{ - struct pt_regs *ptregs = task_pt_regs(tsk); - memcpy(®s->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs)); - memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs)); - regs->orig_gpr2 = ptregs->orig_gpr2; - return 1; -} - -#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs(tsk, regs) - -static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) -{ - if (tsk == current) - save_fp_regs(fpregs); - else - memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t)); - return 1; -} - -#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs) - - /* This yields a mask that user programs can use to figure out what instruction set this CPU supports. */ @@ -204,7 +174,10 @@ do { \ set_personality(PER_SVR4); \ else if (current->personality != PER_LINUX32) \ set_personality(PER_LINUX); \ - clear_thread_flag(TIF_31BIT); \ + if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \ + set_thread_flag(TIF_31BIT); \ + else \ + clear_thread_flag(TIF_31BIT); \ } while (0) #endif /* __s390x__ */ diff --git a/include/asm-s390/etr.h b/include/asm-s390/etr.h index b498f19bb9a..80ef58c6197 100644 --- a/include/asm-s390/etr.h +++ b/include/asm-s390/etr.h @@ -122,7 +122,7 @@ struct etr_aib { } __attribute__ ((packed,aligned(8))); /* ETR interruption parameter */ -struct etr_interruption_parameter { +struct etr_irq_parm { unsigned int _pad0 : 8; unsigned int pc0 : 1; /* port 0 state change */ unsigned int pc1 : 1; /* port 1 state change */ @@ -213,7 +213,46 @@ static inline int etr_ptff(void *ptff_block, unsigned int func) #define ETR_PTFF_SGS 0x43 /* set gross steering rate */ /* Functions needed by the machine check handler */ -extern void etr_switch_to_local(void); -extern void etr_sync_check(void); +void etr_switch_to_local(void); +void etr_sync_check(void); + +/* STP interruption parameter */ +struct stp_irq_parm { + unsigned int _pad0 : 14; + unsigned int tsc : 1; /* Timing status change */ + unsigned int lac : 1; /* Link availability change */ + unsigned int tcpc : 1; /* Time control parameter change */ + unsigned int _pad2 : 15; +} __attribute__ ((packed)); + +#define STP_OP_SYNC 1 +#define STP_OP_CTRL 3 + +struct stp_sstpi { + unsigned int rsvd0; + unsigned int rsvd1 : 8; + unsigned int stratum : 8; + unsigned int vbits : 16; + unsigned int leaps : 16; + unsigned int tmd : 4; + unsigned int ctn : 4; + unsigned int rsvd2 : 3; + unsigned int c : 1; + unsigned int tst : 4; + unsigned int tzo : 16; + unsigned int dsto : 16; + unsigned int ctrl : 16; + unsigned int rsvd3 : 16; + unsigned int tto; + unsigned int rsvd4; + unsigned int ctnid[3]; + unsigned int rsvd5; + unsigned int todoff[4]; + unsigned int rsvd6[48]; +} __attribute__ ((packed)); + +/* Functions needed by the machine check handler */ +void stp_sync_check(void); +void stp_island_check(void); #endif /* __S390_ETR_H */ diff --git a/include/asm-s390/fcx.h b/include/asm-s390/fcx.h new file mode 100644 index 00000000000..8be1f3a5804 --- /dev/null +++ b/include/asm-s390/fcx.h @@ -0,0 +1,311 @@ +/* + * Functions for assembling fcx enabled I/O control blocks. + * + * Copyright IBM Corp. 2008 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#ifndef _ASM_S390_FCX_H +#define _ASM_S390_FCX_H _ASM_S390_FCX_H + +#include <linux/types.h> + +#define TCW_FORMAT_DEFAULT 0 +#define TCW_TIDAW_FORMAT_DEFAULT 0 +#define TCW_FLAGS_INPUT_TIDA 1 << (23 - 5) +#define TCW_FLAGS_TCCB_TIDA 1 << (23 - 6) +#define TCW_FLAGS_OUTPUT_TIDA 1 << (23 - 7) +#define TCW_FLAGS_TIDAW_FORMAT(x) ((x) & 3) << (23 - 9) +#define TCW_FLAGS_GET_TIDAW_FORMAT(x) (((x) >> (23 - 9)) & 3) + +/** + * struct tcw - Transport Control Word (TCW) + * @format: TCW format + * @flags: TCW flags + * @tccbl: Transport-Command-Control-Block Length + * @r: Read Operations + * @w: Write Operations + * @output: Output-Data Address + * @input: Input-Data Address + * @tsb: Transport-Status-Block Address + * @tccb: Transport-Command-Control-Block Address + * @output_count: Output Count + * @input_count: Input Count + * @intrg: Interrogate TCW Address + */ +struct tcw { + u32 format:2; + u32 :6; + u32 flags:24; + u32 :8; + u32 tccbl:6; + u32 r:1; + u32 w:1; + u32 :16; + u64 output; + u64 input; + u64 tsb; + u64 tccb; + u32 output_count; + u32 input_count; + u32 :32; + u32 :32; + u32 :32; + u32 intrg; +} __attribute__ ((packed, aligned(64))); + +#define TIDAW_FLAGS_LAST 1 << (7 - 0) +#define TIDAW_FLAGS_SKIP 1 << (7 - 1) +#define TIDAW_FLAGS_DATA_INT 1 << (7 - 2) +#define TIDAW_FLAGS_TTIC 1 << (7 - 3) +#define TIDAW_FLAGS_INSERT_CBC 1 << (7 - 4) + +/** + * struct tidaw - Transport-Indirect-Addressing Word (TIDAW) + * @flags: TIDAW flags. Can be an arithmetic OR of the following constants: + * %TIDAW_FLAGS_LAST, %TIDAW_FLAGS_SKIP, %TIDAW_FLAGS_DATA_INT, + * %TIDAW_FLAGS_TTIC, %TIDAW_FLAGS_INSERT_CBC + * @count: Count + * @addr: Address + */ +struct tidaw { + u32 flags:8; + u32 :24; + u32 count; + u64 addr; +} __attribute__ ((packed, aligned(16))); + +/** + * struct tsa_iostat - I/O-Status Transport-Status Area (IO-Stat TSA) + * @dev_time: Device Time + * @def_time: Defer Time + * @queue_time: Queue Time + * @dev_busy_time: Device-Busy Time + * @dev_act_time: Device-Active-Only Time + * @sense: Sense Data (if present) + */ +struct tsa_iostat { + u32 dev_time; + u32 def_time; + u32 queue_time; + u32 dev_busy_time; + u32 dev_act_time; + u8 sense[32]; +} __attribute__ ((packed)); + +/** + * struct tsa_ddpcs - Device-Detected-Program-Check Transport-Status Area (DDPC TSA) + * @rc: Reason Code + * @rcq: Reason Code Qualifier + * @sense: Sense Data (if present) + */ +struct tsa_ddpc { + u32 :24; + u32 rc:8; + u8 rcq[16]; + u8 sense[32]; +} __attribute__ ((packed)); + +#define TSA_INTRG_FLAGS_CU_STATE_VALID 1 << (7 - 0) +#define TSA_INTRG_FLAGS_DEV_STATE_VALID 1 << (7 - 1) +#define TSA_INTRG_FLAGS_OP_STATE_VALID 1 << (7 - 2) + +/** + * struct tsa_intrg - Interrogate Transport-Status Area (Intrg. TSA) + * @format: Format + * @flags: Flags. Can be an arithmetic OR of the following constants: + * %TSA_INTRG_FLAGS_CU_STATE_VALID, %TSA_INTRG_FLAGS_DEV_STATE_VALID, + * %TSA_INTRG_FLAGS_OP_STATE_VALID + * @cu_state: Controle-Unit State + * @dev_state: Device State + * @op_state: Operation State + * @sd_info: State-Dependent Information + * @dl_id: Device-Level Identifier + * @dd_data: Device-Dependent Data + */ +struct tsa_intrg { + u32 format:8; + u32 flags:8; + u32 cu_state:8; + u32 dev_state:8; + u32 op_state:8; + u32 :24; + u8 sd_info[12]; + u32 dl_id; + u8 dd_data[28]; +} __attribute__ ((packed)); + +#define TSB_FORMAT_NONE 0 +#define TSB_FORMAT_IOSTAT 1 +#define TSB_FORMAT_DDPC 2 +#define TSB_FORMAT_INTRG 3 + +#define TSB_FLAGS_DCW_OFFSET_VALID 1 << (7 - 0) +#define TSB_FLAGS_COUNT_VALID 1 << (7 - 1) +#define TSB_FLAGS_CACHE_MISS 1 << (7 - 2) +#define TSB_FLAGS_TIME_VALID 1 << (7 - 3) +#define TSB_FLAGS_FORMAT(x) ((x) & 7) +#define TSB_FORMAT(t) ((t)->flags & 7) + +/** + * struct tsb - Transport-Status Block (TSB) + * @length: Length + * @flags: Flags. Can be an arithmetic OR of the following constants: + * %TSB_FLAGS_DCW_OFFSET_VALID, %TSB_FLAGS_COUNT_VALID, %TSB_FLAGS_CACHE_MISS, + * %TSB_FLAGS_TIME_VALID + * @dcw_offset: DCW Offset + * @count: Count + * @tsa: Transport-Status-Area + */ +struct tsb { + u32 length:8; + u32 flags:8; + u32 dcw_offset:16; + u32 count; + u32 :32; + union { + struct tsa_iostat iostat; + struct tsa_ddpc ddpc; + struct tsa_intrg intrg; + } __attribute__ ((packed)) tsa; +} __attribute__ ((packed, aligned(8))); + +#define DCW_INTRG_FORMAT_DEFAULT 0 + +#define DCW_INTRG_RC_UNSPECIFIED 0 +#define DCW_INTRG_RC_TIMEOUT 1 + +#define DCW_INTRG_RCQ_UNSPECIFIED 0 +#define DCW_INTRG_RCQ_PRIMARY 1 +#define DCW_INTRG_RCQ_SECONDARY 2 + +#define DCW_INTRG_FLAGS_MPM 1 < (7 - 0) +#define DCW_INTRG_FLAGS_PPR 1 < (7 - 1) +#define DCW_INTRG_FLAGS_CRIT 1 < (7 - 2) + +/** + * struct dcw_intrg_data - Interrogate DCW data + * @format: Format. Should be %DCW_INTRG_FORMAT_DEFAULT + * @rc: Reason Code. Can be one of %DCW_INTRG_RC_UNSPECIFIED, + * %DCW_INTRG_RC_TIMEOUT + * @rcq: Reason Code Qualifier: Can be one of %DCW_INTRG_RCQ_UNSPECIFIED, + * %DCW_INTRG_RCQ_PRIMARY, %DCW_INTRG_RCQ_SECONDARY + * @lpm: Logical-Path Mask + * @pam: Path-Available Mask + * @pim: Path-Installed Mask + * @timeout: Timeout + * @flags: Flags. Can be an arithmetic OR of %DCW_INTRG_FLAGS_MPM, + * %DCW_INTRG_FLAGS_PPR, %DCW_INTRG_FLAGS_CRIT + * @time: Time + * @prog_id: Program Identifier + * @prog_data: Program-Dependent Data + */ +struct dcw_intrg_data { + u32 format:8; + u32 rc:8; + u32 rcq:8; + u32 lpm:8; + u32 pam:8; + u32 pim:8; + u32 timeout:16; + u32 flags:8; + u32 :24; + u32 :32; + u64 time; + u64 prog_id; + u8 prog_data[0]; +} __attribute__ ((packed)); + +#define DCW_FLAGS_CC 1 << (7 - 1) + +#define DCW_CMD_WRITE 0x01 +#define DCW_CMD_READ 0x02 +#define DCW_CMD_CONTROL 0x03 +#define DCW_CMD_SENSE 0x04 +#define DCW_CMD_SENSE_ID 0xe4 +#define DCW_CMD_INTRG 0x40 + +/** + * struct dcw - Device-Command Word (DCW) + * @cmd: Command Code. Can be one of %DCW_CMD_WRITE, %DCW_CMD_READ, + * %DCW_CMD_CONTROL, %DCW_CMD_SENSE, %DCW_CMD_SENSE_ID, %DCW_CMD_INTRG + * @flags: Flags. Can be an arithmetic OR of %DCW_FLAGS_CC + * @cd_count: Control-Data Count + * @count: Count + * @cd: Control Data + */ +struct dcw { + u32 cmd:8; + u32 flags:8; + u32 :8; + u32 cd_count:8; + u32 count; + u8 cd[0]; +} __attribute__ ((packed)); + +#define TCCB_FORMAT_DEFAULT 0x7f +#define TCCB_MAX_DCW 30 +#define TCCB_MAX_SIZE (sizeof(struct tccb_tcah) + \ + TCCB_MAX_DCW * sizeof(struct dcw) + \ + sizeof(struct tccb_tcat)) +#define TCCB_SAC_DEFAULT 0xf901 +#define TCCB_SAC_INTRG 0xf902 + +/** + * struct tccb_tcah - Transport-Command-Area Header (TCAH) + * @format: Format. Should be %TCCB_FORMAT_DEFAULT + * @tcal: Transport-Command-Area Length + * @sac: Service-Action Code. Can be one of %TCCB_SAC_DEFAULT, %TCCB_SAC_INTRG + * @prio: Priority + */ +struct tccb_tcah { + u32 format:8; + u32 :24; + u32 :24; + u32 tcal:8; + u32 sac:16; + u32 :8; + u32 prio:8; + u32 :32; +} __attribute__ ((packed)); + +/** + * struct tccb_tcat - Transport-Command-Area Trailer (TCAT) + * @count: Transport Count + */ +struct tccb_tcat { + u32 :32; + u32 count; +} __attribute__ ((packed)); + +/** + * struct tccb - (partial) Transport-Command-Control Block (TCCB) + * @tcah: TCAH + * @tca: Transport-Command Area + */ +struct tccb { + struct tccb_tcah tcah; + u8 tca[0]; +} __attribute__ ((packed, aligned(8))); + +struct tcw *tcw_get_intrg(struct tcw *tcw); +void *tcw_get_data(struct tcw *tcw); +struct tccb *tcw_get_tccb(struct tcw *tcw); +struct tsb *tcw_get_tsb(struct tcw *tcw); + +void tcw_init(struct tcw *tcw, int r, int w); +void tcw_finalize(struct tcw *tcw, int num_tidaws); + +void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw); +void tcw_set_data(struct tcw *tcw, void *data, int use_tidal); +void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb); +void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb); + +void tccb_init(struct tccb *tccb, size_t tccb_size, u32 sac); +void tsb_init(struct tsb *tsb); +struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags, + void *cd, u8 cd_count, u32 count); +struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags, + void *addr, u32 count); + +#endif /* _ASM_S390_FCX_H */ diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h index c1b2e50392b..eaca6dff540 100644 --- a/include/asm-s390/ipl.h +++ b/include/asm-s390/ipl.h @@ -56,15 +56,19 @@ struct ipl_block_fcp { u8 scp_data[]; } __attribute__((packed)); +#define DIAG308_VMPARM_SIZE 64 + struct ipl_block_ccw { - u8 load_param[8]; + u8 load_parm[8]; u8 reserved1[84]; u8 reserved2[2]; u16 devno; u8 vm_flags; u8 reserved3[3]; u32 vm_parm_len; - u8 reserved4[80]; + u8 nss_name[8]; + u8 vm_parm[DIAG308_VMPARM_SIZE]; + u8 reserved4[8]; } __attribute__((packed)); struct ipl_parameter_block { @@ -73,7 +77,7 @@ struct ipl_parameter_block { struct ipl_block_fcp fcp; struct ipl_block_ccw ccw; } ipl_info; -} __attribute__((packed)); +} __attribute__((packed,aligned(4096))); /* * IPL validity flags @@ -86,6 +90,8 @@ extern void do_reipl(void); extern void do_halt(void); extern void do_poff(void); extern void ipl_save_parameters(void); +extern void ipl_update_parameters(void); +extern void get_ipl_vmparm(char *); enum { IPL_DEVNO_VALID = 1, @@ -147,6 +153,11 @@ enum diag308_flags { DIAG308_FLAGS_LP_VALID = 0x80, }; +enum diag308_vm_flags { + DIAG308_VM_FLAGS_NSS_VALID = 0x80, + DIAG308_VM_FLAGS_VP_VALID = 0x40, +}; + enum diag308_rc { DIAG308_RC_OK = 1, }; diff --git a/include/asm-s390/isc.h b/include/asm-s390/isc.h new file mode 100644 index 00000000000..34bb8916db4 --- /dev/null +++ b/include/asm-s390/isc.h @@ -0,0 +1,25 @@ +#ifndef _ASM_S390_ISC_H +#define _ASM_S390_ISC_H + +#include <linux/types.h> + +/* + * I/O interruption subclasses used by drivers. + * Please add all used iscs here so that it is possible to distribute + * isc usage between drivers. + * Reminder: 0 is highest priority, 7 lowest. + */ +#define MAX_ISC 7 + +/* Regular I/O interrupts. */ +#define IO_SCH_ISC 3 /* regular I/O subchannels */ +#define CONSOLE_ISC 1 /* console I/O subchannel */ +#define CHSC_SCH_ISC 7 /* CHSC subchannels */ +/* Adapter interrupts. */ +#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */ + +/* Functions for registration of I/O interruption subclasses */ +void isc_register(unsigned int isc); +void isc_unregister(unsigned int isc); + +#endif /* _ASM_S390_ISC_H */ diff --git a/include/asm-s390/itcw.h b/include/asm-s390/itcw.h new file mode 100644 index 00000000000..a9bc5c36b32 --- /dev/null +++ b/include/asm-s390/itcw.h @@ -0,0 +1,30 @@ +/* + * Functions for incremental construction of fcx enabled I/O control blocks. + * + * Copyright IBM Corp. 2008 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#ifndef _ASM_S390_ITCW_H +#define _ASM_S390_ITCW_H _ASM_S390_ITCW_H + +#include <linux/types.h> +#include <asm/fcx.h> + +#define ITCW_OP_READ 0 +#define ITCW_OP_WRITE 1 + +struct itcw; + +struct tcw *itcw_get_tcw(struct itcw *itcw); +size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws); +struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, + int max_tidaws, int intrg_max_tidaws); +struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd, + u8 cd_count, u32 count); +struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, + u32 count); +void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal); +void itcw_finalize(struct itcw *itcw); + +#endif /* _ASM_S390_ITCW_H */ diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index bd0ea191dfa..0bdb704ae05 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -29,6 +29,7 @@ * the S390 page table tree. */ #ifndef __ASSEMBLY__ +#include <linux/sched.h> #include <linux/mm_types.h> #include <asm/bitops.h> #include <asm/bug.h> diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h index a00f79dd323..4af80af2a88 100644 --- a/include/asm-s390/processor.h +++ b/include/asm-s390/processor.h @@ -143,11 +143,19 @@ struct stack_frame { /* * Do necessary setup to start up a new thread. */ -#define start_thread(regs, new_psw, new_stackp) do { \ +#define start_thread(regs, new_psw, new_stackp) do { \ set_fs(USER_DS); \ regs->psw.mask = psw_user_bits; \ - regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ - regs->gprs[15] = new_stackp ; \ + regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ + regs->gprs[15] = new_stackp; \ +} while (0) + +#define start_thread31(regs, new_psw, new_stackp) do { \ + set_fs(USER_DS); \ + regs->psw.mask = psw_user32_bits; \ + regs->psw.addr = new_psw | PSW_ADDR_AMODE; \ + regs->gprs[15] = new_stackp; \ + crst_table_downgrade(current->mm, 1UL << 31); \ } while (0) /* Forward declaration, a strange C thing */ @@ -328,16 +336,6 @@ extern void (*s390_base_mcck_handler_fn)(void); extern void (*s390_base_pgm_handler_fn)(void); extern void (*s390_base_ext_handler_fn)(void); -/* - * CPU idle notifier chain. - */ -#define S390_CPU_IDLE 0 -#define S390_CPU_NOT_IDLE 1 - -struct notifier_block; -int register_idle_notifier(struct notifier_block *nb); -int unregister_idle_notifier(struct notifier_block *nb); - #define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL #endif diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h index d7d4e2eb3e6..af2c9ac28a0 100644 --- a/include/asm-s390/ptrace.h +++ b/include/asm-s390/ptrace.h @@ -215,6 +215,12 @@ typedef struct unsigned long addr; } __attribute__ ((aligned(8))) psw_t; +typedef struct +{ + __u32 mask; + __u32 addr; +} __attribute__ ((aligned(8))) psw_compat_t; + #ifndef __s390x__ #define PSW_MASK_PER 0x40000000UL @@ -292,6 +298,15 @@ typedef struct unsigned long orig_gpr2; } s390_regs; +typedef struct +{ + psw_compat_t psw; + __u32 gprs[NUM_GPRS]; + __u32 acrs[NUM_ACRS]; + __u32 orig_gpr2; +} s390_compat_regs; + + #ifdef __KERNEL__ #include <asm/setup.h> #include <asm/page.h> diff --git a/drivers/s390/cio/schid.h b/include/asm-s390/schid.h index 54328fec5ad..5017ffa78e0 100644 --- a/drivers/s390/cio/schid.h +++ b/include/asm-s390/schid.h @@ -1,12 +1,14 @@ -#ifndef S390_SCHID_H -#define S390_SCHID_H +#ifndef ASM_SCHID_H +#define ASM_SCHID_H struct subchannel_id { - __u32 reserved:13; - __u32 ssid:2; - __u32 one:1; - __u32 sch_no:16; -} __attribute__ ((packed,aligned(4))); + __u32 cssid : 8; + __u32 : 4; + __u32 m : 1; + __u32 ssid : 2; + __u32 one : 1; + __u32 sch_no : 16; +} __attribute__ ((packed, aligned(4))); /* Helper function for sane state of pre-allocated subchannel_id. */ @@ -23,4 +25,4 @@ schid_equal(struct subchannel_id *schid1, struct subchannel_id *schid2) return !memcmp(schid1, schid2, sizeof(struct subchannel_id)); } -#endif /* S390_SCHID_H */ +#endif /* ASM_SCHID_H */ diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index b5f2843013a..fed7bee650a 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -45,9 +45,9 @@ struct sclp_cpu_info { int sclp_get_cpu_info(struct sclp_cpu_info *info); int sclp_cpu_configure(u8 cpu); int sclp_cpu_deconfigure(u8 cpu); -void sclp_read_info_early(void); void sclp_facilities_detect(void); -unsigned long long sclp_memory_detect(void); +unsigned long long sclp_get_rnmax(void); +unsigned long long sclp_get_rzm(void); int sclp_sdias_blk_count(void); int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); int sclp_chp_configure(struct chp_id chpid); diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h index ba69674012a..f09ee3f7297 100644 --- a/include/asm-s390/setup.h +++ b/include/asm-s390/setup.h @@ -8,14 +8,16 @@ #ifndef _ASM_S390_SETUP_H #define _ASM_S390_SETUP_H -#define COMMAND_LINE_SIZE 896 +#define COMMAND_LINE_SIZE 1024 + +#define ARCH_COMMAND_LINE_SIZE 896 #ifdef __KERNEL__ #include <asm/types.h> #define PARMAREA 0x10400 -#define MEMORY_CHUNKS 16 /* max 0x7fff */ +#define MEMORY_CHUNKS 256 #ifndef __ASSEMBLY__ @@ -36,12 +38,14 @@ struct mem_chunk { unsigned long addr; unsigned long size; - unsigned long type; + int type; }; extern struct mem_chunk memory_chunk[]; extern unsigned long real_memory_size; +void detect_memory_layout(struct mem_chunk chunk[]); + #ifdef CONFIG_S390_SWITCH_AMODE extern unsigned int switch_amode; #else @@ -61,7 +65,6 @@ extern unsigned long machine_flags; #define MACHINE_FLAG_VM (1UL << 0) #define MACHINE_FLAG_IEEE (1UL << 1) -#define MACHINE_FLAG_P390 (1UL << 2) #define MACHINE_FLAG_CSP (1UL << 3) #define MACHINE_FLAG_MVPG (1UL << 4) #define MACHINE_FLAG_DIAG44 (1UL << 5) @@ -97,7 +100,6 @@ extern unsigned long machine_flags; #define MACHINE_HAS_PFMF (machine_flags & MACHINE_FLAG_PFMF) #endif /* __s390x__ */ -#define MACHINE_HAS_SCLP (!MACHINE_IS_P390) #define ZFCPDUMP_HSA_SIZE (32UL<<20) /* diff --git a/include/asm-s390/sparsemem.h b/include/asm-s390/sparsemem.h index 06dfdab6c0e..545d219e6a2 100644 --- a/include/asm-s390/sparsemem.h +++ b/include/asm-s390/sparsemem.h @@ -1,15 +1,15 @@ #ifndef _ASM_S390_SPARSEMEM_H #define _ASM_S390_SPARSEMEM_H -#define SECTION_SIZE_BITS 25 - #ifdef CONFIG_64BIT +#define SECTION_SIZE_BITS 28 #define MAX_PHYSADDR_BITS 42 #define MAX_PHYSMEM_BITS 42 #else +#define SECTION_SIZE_BITS 25 #define MAX_PHYSADDR_BITS 31 #define MAX_PHYSMEM_BITS 31 diff --git a/include/asm-s390/timer.h b/include/asm-s390/timer.h index adb34860a54..d98d79e35cd 100644 --- a/include/asm-s390/timer.h +++ b/include/asm-s390/timer.h @@ -48,6 +48,18 @@ extern int del_virt_timer(struct vtimer_list *timer); extern void init_cpu_vtimer(void); extern void vtime_init(void); +#ifdef CONFIG_VIRT_TIMER + +extern void vtime_start_cpu_timer(void); +extern void vtime_stop_cpu_timer(void); + +#else + +static inline void vtime_start_cpu_timer(void) { } +static inline void vtime_stop_cpu_timer(void) { } + +#endif /* CONFIG_VIRT_TIMER */ + #endif /* __KERNEL__ */ #endif /* _ASM_S390_TIMER_H */ diff --git a/include/asm-s390/zcrypt.h b/include/asm-s390/zcrypt.h index f228f1b8687..00d3bbd4411 100644 --- a/include/asm-s390/zcrypt.h +++ b/include/asm-s390/zcrypt.h @@ -29,7 +29,7 @@ #define ZCRYPT_VERSION 2 #define ZCRYPT_RELEASE 1 -#define ZCRYPT_VARIANT 0 +#define ZCRYPT_VARIANT 1 #include <linux/ioctl.h> #include <linux/compiler.h> diff --git a/include/linux/bio.h b/include/linux/bio.h index 61c15eaf3fb..0933a14e641 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -64,6 +64,7 @@ struct bio_vec { struct bio_set; struct bio; +struct bio_integrity_payload; typedef void (bio_end_io_t) (struct bio *, int); typedef void (bio_destructor_t) (struct bio *); @@ -112,6 +113,9 @@ struct bio { atomic_t bi_cnt; /* pin count */ void *bi_private; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + struct bio_integrity_payload *bi_integrity; /* data integrity */ +#endif bio_destructor_t *bi_destructor; /* destructor */ }; @@ -271,6 +275,29 @@ static inline void *bio_data(struct bio *bio) */ #define bio_get(bio) atomic_inc(&(bio)->bi_cnt) +#if defined(CONFIG_BLK_DEV_INTEGRITY) +/* + * bio integrity payload + */ +struct bio_integrity_payload { + struct bio *bip_bio; /* parent bio */ + struct bio_vec *bip_vec; /* integrity data vector */ + + sector_t bip_sector; /* virtual start sector */ + + void *bip_buf; /* generated integrity data */ + bio_end_io_t *bip_end_io; /* saved I/O completion fn */ + + int bip_error; /* saved I/O error */ + unsigned int bip_size; + + unsigned short bip_pool; /* pool the ivec came from */ + unsigned short bip_vcnt; /* # of integrity bio_vecs */ + unsigned short bip_idx; /* current bip_vec index */ + + struct work_struct bip_work; /* I/O completion */ +}; +#endif /* CONFIG_BLK_DEV_INTEGRITY */ /* * A bio_pair is used when we need to split a bio. @@ -283,10 +310,14 @@ static inline void *bio_data(struct bio *bio) * in bio2.bi_private */ struct bio_pair { - struct bio bio1, bio2; - struct bio_vec bv1, bv2; - atomic_t cnt; - int error; + struct bio bio1, bio2; + struct bio_vec bv1, bv2; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + struct bio_integrity_payload bip1, bip2; + struct bio_vec iv1, iv2; +#endif + atomic_t cnt; + int error; }; extern struct bio_pair *bio_split(struct bio *bi, mempool_t *pool, int first_sectors); @@ -333,6 +364,39 @@ extern struct bio *bio_copy_user_iov(struct request_queue *, struct sg_iovec *, int, int); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); +extern struct bio_vec *bvec_alloc_bs(gfp_t, int, unsigned long *, struct bio_set *); +extern unsigned int bvec_nr_vecs(unsigned short idx); + +/* + * bio_set is used to allow other portions of the IO system to + * allocate their own private memory pools for bio and iovec structures. + * These memory pools in turn all allocate from the bio_slab + * and the bvec_slabs[]. + */ +#define BIO_POOL_SIZE 2 +#define BIOVEC_NR_POOLS 6 + +struct bio_set { + mempool_t *bio_pool; +#if defined(CONFIG_BLK_DEV_INTEGRITY) + mempool_t *bio_integrity_pool; +#endif + mempool_t *bvec_pools[BIOVEC_NR_POOLS]; +}; + +struct biovec_slab { + int nr_vecs; + char *name; + struct kmem_cache *slab; +}; + +extern struct bio_set *fs_bio_set; + +/* + * a small number of entries is fine, not going to be performance critical. + * basically we just need to survive + */ +#define BIO_SPLIT_ENTRIES 2 #ifdef CONFIG_HIGHMEM /* @@ -381,5 +445,63 @@ static inline char *__bio_kmap_irq(struct bio *bio, unsigned short idx, __bio_kmap_irq((bio), (bio)->bi_idx, (flags)) #define bio_kunmap_irq(buf,flags) __bio_kunmap_irq(buf, flags) +#if defined(CONFIG_BLK_DEV_INTEGRITY) + +#define bip_vec_idx(bip, idx) (&(bip->bip_vec[(idx)])) +#define bip_vec(bip) bip_vec_idx(bip, 0) + +#define __bip_for_each_vec(bvl, bip, i, start_idx) \ + for (bvl = bip_vec_idx((bip), (start_idx)), i = (start_idx); \ + i < (bip)->bip_vcnt; \ + bvl++, i++) + +#define bip_for_each_vec(bvl, bip, i) \ + __bip_for_each_vec(bvl, bip, i, (bip)->bip_idx) + +static inline int bio_integrity(struct bio *bio) +{ +#if defined(CONFIG_BLK_DEV_INTEGRITY) + return bio->bi_integrity != NULL; +#else + return 0; +#endif +} + +extern struct bio_integrity_payload *bio_integrity_alloc_bioset(struct bio *, gfp_t, unsigned int, struct bio_set *); +extern struct bio_integrity_payload *bio_integrity_alloc(struct bio *, gfp_t, unsigned int); +extern void bio_integrity_free(struct bio *, struct bio_set *); +extern int bio_integrity_add_page(struct bio *, struct page *, unsigned int, unsigned int); +extern int bio_integrity_enabled(struct bio *bio); +extern int bio_integrity_set_tag(struct bio *, void *, unsigned int); +extern int bio_integrity_get_tag(struct bio *, void *, unsigned int); +extern int bio_integrity_prep(struct bio *); +extern void bio_integrity_endio(struct bio *, int); +extern void bio_integrity_advance(struct bio *, unsigned int); +extern void bio_integrity_trim(struct bio *, unsigned int, unsigned int); +extern void bio_integrity_split(struct bio *, struct bio_pair *, int); +extern int bio_integrity_clone(struct bio *, struct bio *, struct bio_set *); +extern int bioset_integrity_create(struct bio_set *, int); +extern void bioset_integrity_free(struct bio_set *); +extern void bio_integrity_init_slab(void); + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +#define bio_integrity(a) (0) +#define bioset_integrity_create(a, b) (0) +#define bio_integrity_prep(a) (0) +#define bio_integrity_enabled(a) (0) +#define bio_integrity_clone(a, b, c) (0) +#define bioset_integrity_free(a) do { } while (0) +#define bio_integrity_free(a, b) do { } while (0) +#define bio_integrity_endio(a, b) do { } while (0) +#define bio_integrity_advance(a, b) do { } while (0) +#define bio_integrity_trim(a, b, c) do { } while (0) +#define bio_integrity_split(a, b, c) do { } while (0) +#define bio_integrity_set_tag(a, b, c) do { } while (0) +#define bio_integrity_get_tag(a, b, c) do { } while (0) +#define bio_integrity_init_slab(a) do { } while (0) + +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + #endif /* CONFIG_BLOCK */ #endif /* __LINUX_BIO_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d2a1b71e93c..1ffd8bfdc4c 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -23,7 +23,6 @@ struct scsi_ioctl_command; struct request_queue; -typedef struct request_queue request_queue_t __deprecated; struct elevator_queue; typedef struct elevator_queue elevator_t; struct request_pm_state; @@ -34,12 +33,6 @@ struct sg_io_hdr; #define BLKDEV_MIN_RQ 4 #define BLKDEV_MAX_RQ 128 /* Default maximum */ -int put_io_context(struct io_context *ioc); -void exit_io_context(void); -struct io_context *get_io_context(gfp_t gfp_flags, int node); -struct io_context *alloc_io_context(gfp_t gfp_flags, int node); -void copy_io_context(struct io_context **pdst, struct io_context **psrc); - struct request; typedef void (rq_end_io_fn)(struct request *, int); @@ -113,6 +106,7 @@ enum rq_flag_bits { __REQ_ALLOCED, /* request came from our alloc pool */ __REQ_RW_META, /* metadata io request */ __REQ_COPY_USER, /* contains copies of user pages */ + __REQ_INTEGRITY, /* integrity metadata has been remapped */ __REQ_NR_BITS, /* stops here */ }; @@ -135,6 +129,7 @@ enum rq_flag_bits { #define REQ_ALLOCED (1 << __REQ_ALLOCED) #define REQ_RW_META (1 << __REQ_RW_META) #define REQ_COPY_USER (1 << __REQ_COPY_USER) +#define REQ_INTEGRITY (1 << __REQ_INTEGRITY) #define BLK_MAX_CDB 16 @@ -259,7 +254,14 @@ typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unplug_fn) (struct request_queue *); struct bio_vec; -typedef int (merge_bvec_fn) (struct request_queue *, struct bio *, struct bio_vec *); +struct bvec_merge_data { + struct block_device *bi_bdev; + sector_t bi_sector; + unsigned bi_size; + unsigned long bi_rw; +}; +typedef int (merge_bvec_fn) (struct request_queue *, struct bvec_merge_data *, + struct bio_vec *); typedef void (prepare_flush_fn) (struct request_queue *, struct request *); typedef void (softirq_done_fn)(struct request *); typedef int (dma_drain_needed_fn)(struct request *); @@ -426,6 +428,32 @@ static inline void queue_flag_set_unlocked(unsigned int flag, __set_bit(flag, &q->queue_flags); } +static inline int queue_flag_test_and_clear(unsigned int flag, + struct request_queue *q) +{ + WARN_ON_ONCE(!queue_is_locked(q)); + + if (test_bit(flag, &q->queue_flags)) { + __clear_bit(flag, &q->queue_flags); + return 1; + } + + return 0; +} + +static inline int queue_flag_test_and_set(unsigned int flag, + struct request_queue *q) +{ + WARN_ON_ONCE(!queue_is_locked(q)); + + if (!test_bit(flag, &q->queue_flags)) { + __set_bit(flag, &q->queue_flags); + return 0; + } + + return 1; +} + static inline void queue_flag_set(unsigned int flag, struct request_queue *q) { WARN_ON_ONCE(!queue_is_locked(q)); @@ -676,7 +704,6 @@ extern int blk_execute_rq(struct request_queue *, struct gendisk *, struct request *, int); extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, struct request *, int, rq_end_io_fn *); -extern int blk_verify_command(unsigned char *, int); extern void blk_unplug(struct request_queue *q); static inline struct request_queue *bdev_get_queue(struct block_device *bdev) @@ -749,6 +776,7 @@ extern void blk_queue_max_segment_size(struct request_queue *, unsigned int); extern void blk_queue_hardsect_size(struct request_queue *, unsigned short); extern void blk_queue_stack_limits(struct request_queue *t, struct request_queue *b); extern void blk_queue_dma_pad(struct request_queue *, unsigned int); +extern void blk_queue_update_dma_pad(struct request_queue *, unsigned int); extern int blk_queue_dma_drain(struct request_queue *q, dma_drain_needed_fn *dma_drain_needed, void *buf, unsigned int size); @@ -802,6 +830,15 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt, extern int blkdev_issue_flush(struct block_device *, sector_t *); +/* +* command filter functions +*/ +extern int blk_verify_command(struct file *file, unsigned char *cmd); +extern int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, + unsigned char *cmd, mode_t *f_mode); +extern int blk_register_filter(struct gendisk *disk); +extern void blk_unregister_filter(struct gendisk *disk); + #define MAX_PHYS_SEGMENTS 128 #define MAX_HW_SEGMENTS 128 #define SAFE_MAX_SECTORS 255 @@ -865,28 +902,116 @@ void kblockd_flush_work(struct work_struct *work); #define MODULE_ALIAS_BLOCKDEV_MAJOR(major) \ MODULE_ALIAS("block-major-" __stringify(major) "-*") +#if defined(CONFIG_BLK_DEV_INTEGRITY) -#else /* CONFIG_BLOCK */ -/* - * stubs for when the block layer is configured out - */ -#define buffer_heads_over_limit 0 +#define INTEGRITY_FLAG_READ 2 /* verify data integrity on read */ +#define INTEGRITY_FLAG_WRITE 4 /* generate data integrity on write */ -static inline long nr_blockdev_pages(void) +struct blk_integrity_exchg { + void *prot_buf; + void *data_buf; + sector_t sector; + unsigned int data_size; + unsigned short sector_size; + const char *disk_name; +}; + +typedef void (integrity_gen_fn) (struct blk_integrity_exchg *); +typedef int (integrity_vrfy_fn) (struct blk_integrity_exchg *); +typedef void (integrity_set_tag_fn) (void *, void *, unsigned int); +typedef void (integrity_get_tag_fn) (void *, void *, unsigned int); + +struct blk_integrity { + integrity_gen_fn *generate_fn; + integrity_vrfy_fn *verify_fn; + integrity_set_tag_fn *set_tag_fn; + integrity_get_tag_fn *get_tag_fn; + + unsigned short flags; + unsigned short tuple_size; + unsigned short sector_size; + unsigned short tag_size; + + const char *name; + + struct kobject kobj; +}; + +extern int blk_integrity_register(struct gendisk *, struct blk_integrity *); +extern void blk_integrity_unregister(struct gendisk *); +extern int blk_integrity_compare(struct block_device *, struct block_device *); +extern int blk_rq_map_integrity_sg(struct request *, struct scatterlist *); +extern int blk_rq_count_integrity_sg(struct request *); + +static inline unsigned short blk_integrity_tuple_size(struct blk_integrity *bi) { + if (bi) + return bi->tuple_size; + return 0; } -static inline void exit_io_context(void) +static inline struct blk_integrity *bdev_get_integrity(struct block_device *bdev) { + return bdev->bd_disk->integrity; } -struct io_context; -static inline int put_io_context(struct io_context *ioc) +static inline unsigned int bdev_get_tag_size(struct block_device *bdev) { - return 1; + struct blk_integrity *bi = bdev_get_integrity(bdev); + + if (bi) + return bi->tag_size; + + return 0; +} + +static inline int bdev_integrity_enabled(struct block_device *bdev, int rw) +{ + struct blk_integrity *bi = bdev_get_integrity(bdev); + + if (bi == NULL) + return 0; + + if (rw == READ && bi->verify_fn != NULL && + (bi->flags & INTEGRITY_FLAG_READ)) + return 1; + + if (rw == WRITE && bi->generate_fn != NULL && + (bi->flags & INTEGRITY_FLAG_WRITE)) + return 1; + + return 0; } +static inline int blk_integrity_rq(struct request *rq) +{ + return bio_integrity(rq->bio); +} + +#else /* CONFIG_BLK_DEV_INTEGRITY */ + +#define blk_integrity_rq(rq) (0) +#define blk_rq_count_integrity_sg(a) (0) +#define blk_rq_map_integrity_sg(a, b) (0) +#define bdev_get_integrity(a) (0) +#define bdev_get_tag_size(a) (0) +#define blk_integrity_compare(a, b) (0) +#define blk_integrity_register(a, b) (0) +#define blk_integrity_unregister(a) do { } while (0); + +#endif /* CONFIG_BLK_DEV_INTEGRITY */ + +#else /* CONFIG_BLOCK */ +/* + * stubs for when the block layer is configured out + */ +#define buffer_heads_over_limit 0 + +static inline long nr_blockdev_pages(void) +{ + return 0; +} #endif /* CONFIG_BLOCK */ diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index e3ef903aae8..d084b8d227a 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -129,6 +129,7 @@ struct blk_trace { u32 dev; struct dentry *dir; struct dentry *dropped_file; + struct dentry *msg_file; atomic_t dropped; }; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ae7aec3cabe..e8787417f65 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -110,6 +110,14 @@ struct hd_struct { #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 #define GENHD_FL_FAIL 64 +#define BLK_SCSI_MAX_CMDS (256) +#define BLK_SCSI_CMD_PER_LONG (BLK_SCSI_MAX_CMDS / (sizeof(long) * 8)) + +struct blk_scsi_cmd_filter { + unsigned long read_ok[BLK_SCSI_CMD_PER_LONG]; + unsigned long write_ok[BLK_SCSI_CMD_PER_LONG]; + struct kobject kobj; +}; struct gendisk { int major; /* major number of driver */ @@ -120,6 +128,7 @@ struct gendisk { struct hd_struct **part; /* [indexed by minor] */ struct block_device_operations *fops; struct request_queue *queue; + struct blk_scsi_cmd_filter cmd_filter; void *private_data; sector_t capacity; @@ -141,6 +150,9 @@ struct gendisk { struct disk_stats dkstats; #endif struct work_struct async_notify; +#ifdef CONFIG_BLK_DEV_INTEGRITY + struct blk_integrity *integrity; +#endif }; /* diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index 2b7a1187cb2..08b987bccf8 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -99,4 +99,22 @@ static inline struct io_context *ioc_task_link(struct io_context *ioc) return NULL; } +#ifdef CONFIG_BLOCK +int put_io_context(struct io_context *ioc); +void exit_io_context(void); +struct io_context *get_io_context(gfp_t gfp_flags, int node); +struct io_context *alloc_io_context(gfp_t gfp_flags, int node); +void copy_io_context(struct io_context **pdst, struct io_context **psrc); +#else +static inline void exit_io_context(void) +{ +} + +struct io_context; +static inline int put_io_context(struct io_context *ioc) +{ + return 1; +} +#endif + #endif diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 69b2342d5eb..c4db5827963 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -159,6 +159,15 @@ struct ap_device_id { #define AP_DEVICE_ID_MATCH_DEVICE_TYPE 0x01 +/* s390 css bus devices (subchannels) */ +struct css_device_id { + __u8 match_flags; + __u8 type; /* subchannel type */ + __u16 pad2; + __u32 pad3; + kernel_ulong_t driver_data; +}; + #define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */ /* to workaround crosscompile issues */ diff --git a/include/pcmcia/bulkmem.h b/include/pcmcia/bulkmem.h deleted file mode 100644 index 6bc7472293b..00000000000 --- a/include/pcmcia/bulkmem.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * bulkmem.h -- Definitions for bulk memory services - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * The initial developer of the original code is David A. Hinds - * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - * (C) 1999 David A. Hinds - */ - -#ifndef _LINUX_BULKMEM_H -#define _LINUX_BULKMEM_H - -/* For GetFirstRegion and GetNextRegion */ -typedef struct region_info_t { - u_int Attributes; - u_int CardOffset; - u_int RegionSize; - u_int AccessSpeed; - u_int BlockSize; - u_int PartMultiple; - u_char JedecMfr, JedecInfo; - memory_handle_t next; -} region_info_t; - -#define REGION_TYPE 0x0001 -#define REGION_TYPE_CM 0x0000 -#define REGION_TYPE_AM 0x0001 -#define REGION_PREFETCH 0x0008 -#define REGION_CACHEABLE 0x0010 -#define REGION_BAR_MASK 0xe000 -#define REGION_BAR_SHIFT 13 - -int pcmcia_get_first_region(struct pcmcia_device *handle, region_info_t *rgn); -int pcmcia_get_next_region(struct pcmcia_device *handle, region_info_t *rgn); - -#endif /* _LINUX_BULKMEM_H */ diff --git a/include/pcmcia/cistpl.h b/include/pcmcia/cistpl.h index d3bbb19caf8..e2e10c1e9a0 100644 --- a/include/pcmcia/cistpl.h +++ b/include/pcmcia/cistpl.h @@ -595,7 +595,7 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple); int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse); -int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, cisinfo_t *info); +int pccard_validate_cis(struct pcmcia_socket *s, unsigned int function, unsigned int *count); /* ... but use these wrappers instead */ #define pcmcia_get_first_tuple(p_dev, tuple) \ diff --git a/include/pcmcia/cs.h b/include/pcmcia/cs.h index 87a260e3699..45d84b27578 100644 --- a/include/pcmcia/cs.h +++ b/include/pcmcia/cs.h @@ -373,9 +373,6 @@ struct pcmcia_socket; int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, conf_reg_t *reg); int pcmcia_get_configuration_info(struct pcmcia_device *p_dev, config_info_t *config); -int pcmcia_get_first_window(window_handle_t *win, win_req_t *req); -int pcmcia_get_next_window(window_handle_t *win, win_req_t *req); -int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status); int pcmcia_get_mem_page(window_handle_t win, memreq_t *req); int pcmcia_map_mem_page(window_handle_t win, memreq_t *req); int pcmcia_modify_configuration(struct pcmcia_device *p_dev, modconf_t *mod); diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h index 9a6bcc4952f..f402a0f435b 100644 --- a/include/pcmcia/cs_types.h +++ b/include/pcmcia/cs_types.h @@ -21,7 +21,8 @@ #include <sys/types.h> #endif -#if defined(__arm__) || defined(__mips__) || defined(__avr32__) +#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || \ + defined(__bfin__) /* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */ typedef u_int ioaddr_t; #else @@ -33,9 +34,6 @@ typedef u_int event_t; typedef u_char cisdata_t; typedef u_short page_t; -struct pcmcia_device; -typedef struct pcmcia_device *client_handle_t; - struct window_t; typedef struct window_t *window_handle_t; diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index f047a1fd64f..b316027c853 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -20,7 +20,6 @@ #include <linux/mod_devicetable.h> #endif -#include <pcmcia/bulkmem.h> #include <pcmcia/cs_types.h> #include <pcmcia/device_id.h> @@ -51,6 +50,24 @@ typedef struct mtd_info_t { u_int CardOffset; } mtd_info_t; +typedef struct region_info_t { + u_int Attributes; + u_int CardOffset; + u_int RegionSize; + u_int AccessSpeed; + u_int BlockSize; + u_int PartMultiple; + u_char JedecMfr, JedecInfo; + memory_handle_t next; +} region_info_t; +#define REGION_TYPE 0x0001 +#define REGION_TYPE_CM 0x0000 +#define REGION_TYPE_AM 0x0001 +#define REGION_PREFETCH 0x0008 +#define REGION_CACHEABLE 0x0010 +#define REGION_BAR_MASK 0xe000 +#define REGION_BAR_SHIFT 13 + typedef union ds_ioctl_arg_t { adjust_t adjust; config_info_t config; diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index f95dca077c1..ed919dd9bb5 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -21,7 +21,6 @@ #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #ifdef CONFIG_CARDBUS #include <linux/pci.h> #endif @@ -136,8 +135,14 @@ struct pccard_resource_ops { struct resource* (*find_mem) (unsigned long base, unsigned long num, unsigned long align, int low, struct pcmcia_socket *s); - int (*adjust_resource) (struct pcmcia_socket *s, - adjust_t *adj); + int (*add_io) (struct pcmcia_socket *s, + unsigned int action, + unsigned long r_start, + unsigned long r_end); + int (*add_mem) (struct pcmcia_socket *s, + unsigned int action, + unsigned long r_start, + unsigned long r_end); int (*init) (struct pcmcia_socket *s); void (*exit) (struct pcmcia_socket *s); }; @@ -245,7 +250,6 @@ struct pcmcia_socket { struct task_struct *thread; struct completion thread_done; - wait_queue_head_t thread_wait; spinlock_t thread_lock; /* protects thread_events */ unsigned int thread_events; diff --git a/include/pcmcia/version.h b/include/pcmcia/version.h deleted file mode 100644 index 5ad9c5e198b..00000000000 --- a/include/pcmcia/version.h +++ /dev/null @@ -1,3 +0,0 @@ -/* version.h 1.94 2000/10/03 17:55:48 (David Hinds) */ - -/* This file will be removed, please don't include it */ diff --git a/kernel/exit.c b/kernel/exit.c index 8f6185e69b6..ceb25878283 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -13,6 +13,7 @@ #include <linux/personality.h> #include <linux/tty.h> #include <linux/mnt_namespace.h> +#include <linux/iocontext.h> #include <linux/key.h> #include <linux/security.h> #include <linux/cpu.h> diff --git a/kernel/fork.c b/kernel/fork.c index 19908b26cf8..b71ccd09fc8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -23,6 +23,7 @@ #include <linux/sem.h> #include <linux/file.h> #include <linux/fdtable.h> +#include <linux/iocontext.h> #include <linux/key.h> #include <linux/binfmts.h> #include <linux/mman.h> diff --git a/mm/Kconfig b/mm/Kconfig index 3aa819d628c..4242743b981 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -129,7 +129,7 @@ config MEMORY_HOTPLUG bool "Allow for memory hot-add" depends on SPARSEMEM || X86_64_ACPI_NUMA depends on HOTPLUG && !HIBERNATION && ARCH_ENABLE_MEMORY_HOTPLUG - depends on (IA64 || X86 || PPC64 || SUPERH) + depends on (IA64 || X86 || PPC64 || SUPERH || S390) comment "Memory hotplug is currently incompatible with Software Suspend" depends on SPARSEMEM && HOTPLUG && HIBERNATION diff --git a/mm/slub.c b/mm/slub.c index 315c392253c..5f6e2c4a2ba 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -431,9 +431,8 @@ static void print_track(const char *s, struct track *t) if (!t->addr) return; - printk(KERN_ERR "INFO: %s in ", s); - __print_symbol("%s", (unsigned long)t->addr); - printk(" age=%lu cpu=%u pid=%d\n", jiffies - t->when, t->cpu, t->pid); + printk(KERN_ERR "INFO: %s in %pS age=%lu cpu=%u pid=%d\n", + s, t->addr, jiffies - t->when, t->cpu, t->pid); } static void print_tracking(struct kmem_cache *s, void *object) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 7b0038f45b1..bda71015885 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1135,8 +1135,7 @@ static void iucv_callback_txdone(struct iucv_path *path, if (this) kfree_skb(this); } - if (!this) - printk(KERN_ERR "AF_IUCV msg tag %u not found\n", msg->tag); + BUG_ON(!this); if (sk->sk_state == IUCV_CLOSING) { if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) { @@ -1196,7 +1195,7 @@ static int __init afiucv_init(void) } cpcmd("QUERY USERID", iucv_userid, sizeof(iucv_userid), &err); if (unlikely(err)) { - printk(KERN_ERR "AF_IUCV needs the VM userid\n"); + WARN_ON(err); err = -EPROTONOSUPPORT; goto out; } @@ -1210,7 +1209,6 @@ static int __init afiucv_init(void) err = sock_register(&iucv_sock_family_ops); if (err) goto out_proto; - printk(KERN_INFO "AF_IUCV lowlevel driver initialized\n"); return 0; out_proto: @@ -1226,8 +1224,6 @@ static void __exit afiucv_exit(void) sock_unregister(PF_IUCV); proto_unregister(&iucv_proto); iucv_unregister(&af_iucv_handler, 0); - - printk(KERN_INFO "AF_IUCV lowlevel driver unloaded\n"); } module_init(afiucv_init); diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 91897076213..7f82b761621 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -1559,16 +1559,11 @@ static void iucv_external_interrupt(u16 code) p = iucv_irq_data[smp_processor_id()]; if (p->ippathid >= iucv_max_pathid) { - printk(KERN_WARNING "iucv_do_int: Got interrupt with " - "pathid %d > max_connections (%ld)\n", - p->ippathid, iucv_max_pathid - 1); + WARN_ON(p->ippathid >= iucv_max_pathid); iucv_sever_pathid(p->ippathid, iucv_error_no_listener); return; } - if (p->iptype < 0x01 || p->iptype > 0x09) { - printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n"); - return; - } + BUG_ON(p->iptype < 0x01 || p->iptype > 0x09); work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC); if (!work) { printk(KERN_WARNING "iucv_external_interrupt: out of memory\n"); diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index cea4a790e1e..37d5c363fbc 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -304,6 +304,14 @@ static int do_ap_entry(const char *filename, return 1; } +/* looks like: "css:tN" */ +static int do_css_entry(const char *filename, + struct css_device_id *id, char *alias) +{ + sprintf(alias, "css:t%01X", id->type); + return 1; +} + /* Looks like: "serio:tyNprNidNexN" */ static int do_serio_entry(const char *filename, struct serio_device_id *id, char *alias) @@ -680,6 +688,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct ap_device_id), "ap", do_ap_entry, mod); + else if (sym_is(symname, "__mod_css_device_table")) + do_table(symval, sym->st_size, + sizeof(struct css_device_id), "css", + do_css_entry, mod); else if (sym_is(symname, "__mod_serio_device_table")) do_table(symval, sym->st_size, sizeof(struct serio_device_id), "serio", |