diff options
Diffstat (limited to 'drivers/s390')
58 files changed, 925 insertions, 934 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index bfeca57098fa..e6bfce690ca3 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1187,7 +1187,7 @@ dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data) static void __dasd_process_blk_queue(struct dasd_device * device) { - request_queue_t *queue; + struct request_queue *queue; struct request *req; struct dasd_ccw_req *cqr; int nr_queued; @@ -1740,7 +1740,7 @@ dasd_cancel_req(struct dasd_ccw_req *cqr) * Dasd request queue function. Called from ll_rw_blk.c */ static void -do_dasd_request(request_queue_t * queue) +do_dasd_request(struct request_queue * queue) { struct dasd_device *device; diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 6a89cefe99bb..0c67258fb9ec 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -291,7 +291,7 @@ dasd_parse_keyword( char *parsestring ) { dasd_page_cache = kmem_cache_create("dasd_page_cache", PAGE_SIZE, PAGE_SIZE, SLAB_CACHE_DMA, - NULL, NULL ); + NULL); if (!dasd_page_cache) MESSAGE(KERN_WARNING, "%s", "Failed to create slab, " "fixed buffer mode disabled."); diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index eccac1c3b71b..d32c60dbdd82 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -24,6 +24,7 @@ #include <asm/s390_ext.h> #include <asm/todclk.h> #include <asm/vtoc.h> +#include <asm/diag.h> #include "dasd_int.h" #include "dasd_diag.h" diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 418b4e63a4fa..ea63ba7828f9 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -555,7 +555,7 @@ dasd_eckd_read_conf(struct dasd_device *device) if (conf_data == NULL) { MESSAGE(KERN_WARNING, "%s", "No configuration " "data retrieved"); - continue; /* no errror */ + continue; /* no error */ } if (conf_len != sizeof (struct dasd_eckd_confdata)) { MESSAGE(KERN_WARNING, @@ -564,7 +564,7 @@ dasd_eckd_read_conf(struct dasd_device *device) conf_len, sizeof (struct dasd_eckd_confdata)); kfree(conf_data); - continue; /* no errror */ + continue; /* no error */ } /* save first valid configuration data */ if (!conf_data_saved){ diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 241294cba415..aeda52682446 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -293,7 +293,7 @@ struct dasd_uid { struct dasd_device { /* Block device stuff. */ struct gendisk *gdp; - request_queue_t *request_queue; + struct request_queue *request_queue; spinlock_t request_queue_lock; struct block_device *bdev; unsigned int devindex; diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 8b3b0f4a157c..ac7e8ef504cb 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -28,6 +28,7 @@ static struct proc_dir_entry *dasd_proc_root_entry = NULL; static struct proc_dir_entry *dasd_devices_entry = NULL; static struct proc_dir_entry *dasd_statistics_entry = NULL; +#ifdef CONFIG_DASD_PROFILE static char * dasd_get_user_string(const char __user *user_buf, size_t user_len) { @@ -47,6 +48,7 @@ dasd_get_user_string(const char __user *user_buf, size_t user_len) buffer[user_len] = 0; return buffer; } +#endif /* CONFIG_DASD_PROFILE */ static int dasd_devices_show(struct seq_file *m, void *v) @@ -167,6 +169,7 @@ dasd_calc_metrics(char *page, char **start, off_t off, return len; } +#ifdef CONFIG_DASD_PROFILE static char * dasd_statistics_array(char *str, unsigned int *array, int shift) { @@ -180,6 +183,7 @@ dasd_statistics_array(char *str, unsigned int *array, int shift) str += sprintf(str,"\n"); return str; } +#endif /* CONFIG_DASD_PROFILE */ static int dasd_statistics_read(char *page, char **start, off_t off, diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 1340451ea408..4d8798bacf97 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -621,7 +621,7 @@ out: } static int -dcssblk_make_request(request_queue_t *q, struct bio *bio) +dcssblk_make_request(struct request_queue *q, struct bio *bio) { struct dcssblk_dev_info *dev_info; struct bio_vec *bvec; @@ -747,14 +747,9 @@ dcssblk_check_params(void) static void __exit dcssblk_exit(void) { - int rc; - PRINT_DEBUG("DCSSBLOCK EXIT...\n"); s390_root_dev_unregister(dcssblk_root_dev); - rc = unregister_blkdev(dcssblk_major, DCSSBLK_NAME); - if (rc) { - PRINT_ERR("unregister_blkdev() failed!\n"); - } + unregister_blkdev(dcssblk_major, DCSSBLK_NAME); PRINT_DEBUG("...finished!\n"); } diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index a04d9120cef0..354a060e5bec 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -191,7 +191,7 @@ static unsigned long __init xpram_highest_page_index(void) /* * Block device make request function. */ -static int xpram_make_request(request_queue_t *q, struct bio *bio) +static int xpram_make_request(struct request_queue *q, struct bio *bio) { xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data; struct bio_vec *bvec; diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 66102a184322..643033890e34 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -44,15 +44,9 @@ config CCW_CONSOLE depends on TN3215_CONSOLE || TN3270_CONSOLE default y -config SCLP - bool "Support for SCLP" - depends on S390 - help - Include support for the SCLP interface to the service element. - config SCLP_TTY bool "Support for SCLP line mode terminal" - depends on SCLP + depends on S390 help Include support for IBM SCLP line-mode terminals. @@ -65,7 +59,7 @@ config SCLP_CONSOLE config SCLP_VT220_TTY bool "Support for SCLP VT220-compatible terminal" - depends on SCLP + depends on S390 help Include support for an IBM SCLP VT220-compatible terminal. @@ -78,7 +72,7 @@ config SCLP_VT220_CONSOLE config SCLP_CPI tristate "Control-Program Identification" - depends on SCLP + depends on S390 help This option enables the hardware console interface for system identification. This is commonly used for workload management and @@ -164,3 +158,10 @@ config MONWRITER help Character device driver for writing z/VM monitor service records +config S390_VMUR + tristate "z/VM unit record device driver" + depends on S390 + default "m" + help + Character device driver for z/VM reader, puncher and printer. + diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index c210784bdf46..130de19916f2 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o +obj-$(CONFIG_S390_VMUR) += vmur.o zcore_mod-objs := sclp_sdias.o zcore.o obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 268598ef3efe..20442fbf9346 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -17,6 +17,7 @@ #include <linux/miscdevice.h> #include <linux/ctype.h> #include <linux/poll.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/ebcdic.h> #include <asm/io.h> @@ -41,6 +42,7 @@ struct mon_private { size_t hdr_to_read; size_t data_to_read; struct mon_buf *current_buf; + struct mutex thread_mutex; }; /* @@ -179,6 +181,7 @@ static int monwrite_open(struct inode *inode, struct file *filp) return -ENOMEM; INIT_LIST_HEAD(&monpriv->list); monpriv->hdr_to_read = sizeof(monpriv->hdr); + mutex_init(&monpriv->thread_mutex); filp->private_data = monpriv; return nonseekable_open(inode, filp); } @@ -209,6 +212,7 @@ static ssize_t monwrite_write(struct file *filp, const char __user *data, void *to; int rc; + mutex_lock(&monpriv->thread_mutex); for (written = 0; written < count; ) { if (monpriv->hdr_to_read) { len = min(count - written, monpriv->hdr_to_read); @@ -247,11 +251,13 @@ static ssize_t monwrite_write(struct file *filp, const char __user *data, } monpriv->hdr_to_read = sizeof(monpriv->hdr); } + mutex_unlock(&monpriv->thread_mutex); return written; out_error: monpriv->data_to_read = 0; monpriv->hdr_to_read = sizeof(struct monwrite_hdr); + mutex_unlock(&monpriv->thread_mutex); return rc; } diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 743944ad61ec..2edd5fb6d3dc 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -21,6 +21,7 @@ #include <asm/ccwdev.h> #include <asm/cio.h> #include <asm/ebcdic.h> +#include <asm/diag.h> #include "raw3270.h" @@ -147,8 +148,7 @@ raw3270_request_alloc(size_t size) * Allocate a new 3270 ccw request from bootmem. Only works very * early in the boot process. Only con3270.c should be using this. */ -struct raw3270_request * -raw3270_request_alloc_bootmem(size_t size) +struct raw3270_request __init *raw3270_request_alloc_bootmem(size_t size) { struct raw3270_request *rq; @@ -848,8 +848,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) /* * Setup 3270 device configured as console. */ -struct raw3270 * -raw3270_setup_console(struct ccw_device *cdev) +struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) { struct raw3270 *rp; char *ascebc; diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index dbb99d1b6f57..c7318a125852 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -72,6 +72,18 @@ typedef unsigned int sclp_cmdw_t; typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ +struct sccb_header { + u16 length; + u8 function_code; + u8 control_mask[3]; + u16 response_code; +} __attribute__((packed)); + +extern u64 sclp_facilities; + +#define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) +#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) + struct gds_subvector { u8 length; u8 key; diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c index a66b914519b5..c68f5e7e63a0 100644 --- a/drivers/s390/char/sclp_chp.c +++ b/drivers/s390/char/sclp_chp.c @@ -55,6 +55,8 @@ static int do_configure(sclp_cmdw_t cmd) struct chp_cfg_data *data; int rc; + if (!SCLP_HAS_CHP_RECONFIG) + return -EOPNOTSUPP; /* Prepare sccb. */ data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!data) @@ -152,6 +154,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info) struct chp_info_data *data; int rc; + if (!SCLP_HAS_CHP_INFO) + return -EOPNOTSUPP; /* Prepare sccb. */ data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!data) diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c index 7bcbe643b087..a1136e052750 100644 --- a/drivers/s390/char/sclp_info.c +++ b/drivers/s390/char/sclp_info.c @@ -11,47 +11,106 @@ #include <asm/sclp.h> #include "sclp.h" -struct sclp_readinfo_sccb s390_readinfo_sccb; +struct sclp_readinfo_sccb { + struct sccb_header header; /* 0-7 */ + u16 rnmax; /* 8-9 */ + u8 rnsize; /* 10 */ + u8 _reserved0[24 - 11]; /* 11-23 */ + u8 loadparm[8]; /* 24-31 */ + u8 _reserved1[48 - 32]; /* 32-47 */ + u64 facilities; /* 48-55 */ + u8 _reserved2[91 - 56]; /* 56-90 */ + u8 flags; /* 91 */ + u8 _reserved3[100 - 92]; /* 92-99 */ + u32 rnsize2; /* 100-103 */ + u64 rnmax2; /* 104-111 */ + u8 _reserved4[4096 - 112]; /* 112-4095 */ +} __attribute__((packed, aligned(4096))); + +static struct sclp_readinfo_sccb __initdata early_readinfo_sccb; +static int __initdata early_readinfo_sccb_valid; + +u64 sclp_facilities; void __init sclp_readinfo_early(void) { - sclp_cmdw_t command; - struct sccb_header *sccb; int ret; + int i; + struct sclp_readinfo_sccb *sccb; + sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, + SCLP_CMDW_READ_SCP_INFO}; - __ctl_set_bit(0, 9); /* enable service signal subclass mask */ - - sccb = &s390_readinfo_sccb.header; - command = SCLP_CMDW_READ_SCP_INFO_FORCED; - while (1) { - u16 response; - - memset(&s390_readinfo_sccb, 0, sizeof(s390_readinfo_sccb)); - sccb->length = sizeof(s390_readinfo_sccb); - sccb->control_mask[2] = 0x80; - - ret = sclp_service_call(command, &s390_readinfo_sccb); - - if (ret == -EIO) - goto out; - if (ret == -EBUSY) - continue; + /* Enable service signal subclass mask. */ + __ctl_set_bit(0, 9); + sccb = &early_readinfo_sccb; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.control_mask[2] = 0x80; + ret = sclp_service_call(commands[i], sccb); + } while (ret == -EBUSY); + if (ret) + break; __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | PSW_MASK_WAIT | PSW_DEFAULT_KEY); local_irq_disable(); + /* + * Contents of the sccb might have changed + * therefore a barrier is needed. + */ barrier(); + if (sccb->header.response_code == 0x10) { + early_readinfo_sccb_valid = 1; + break; + } + if (sccb->header.response_code != 0x1f0) + break; + } + /* Disable service signal subclass mask again. */ + __ctl_clear_bit(0, 9); +} - response = sccb->response_code; +void __init sclp_facilities_detect(void) +{ + if (!early_readinfo_sccb_valid) + return; + sclp_facilities = early_readinfo_sccb.facilities; +} - if (response == 0x10) - break; +unsigned long long __init sclp_memory_detect(void) +{ + unsigned long long memsize; + struct sclp_readinfo_sccb *sccb; - if (response != 0x1f0 || command == SCLP_CMDW_READ_SCP_INFO) - break; + if (!early_readinfo_sccb_valid) + return 0; + sccb = &early_readinfo_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; +} - command = SCLP_CMDW_READ_SCP_INFO; - } -out: - __ctl_clear_bit(0, 9); /* disable service signal subclass mask */ +/* + * This function will be called after sclp_memory_detect(), which gets called + * early from early.c code. Therefore the sccb should have valid contents. + */ +void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +{ + struct sclp_readinfo_sccb *sccb; + + if (!early_readinfo_sccb_valid) + return; + sccb = &early_readinfo_sccb; + info->is_valid = 1; + if (sccb->flags & 0x2) + info->has_dump = 1; + memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); } diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 726334757bbf..40cd21bc5cc4 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -621,11 +621,24 @@ sclp_vt220_flush_buffer(struct tty_struct *tty) /* * Initialize all relevant components and register driver with system. */ -static int -__sclp_vt220_init(int early) +static void __init __sclp_vt220_cleanup(void) +{ + struct list_head *page, *p; + + list_for_each_safe(page, p, &sclp_vt220_empty) { + list_del(page); + if (slab_is_available()) + free_page((unsigned long) page); + else + free_bootmem((unsigned long) page, PAGE_SIZE); + } +} + +static int __init __sclp_vt220_init(void) { void *page; int i; + int num_pages; if (sclp_vt220_initialized) return 0; @@ -642,13 +655,16 @@ __sclp_vt220_init(int early) sclp_vt220_flush_later = 0; /* Allocate pages for output buffering */ - for (i = 0; i < (early ? MAX_CONSOLE_PAGES : MAX_KMEM_PAGES); i++) { - if (early) - page = alloc_bootmem_low_pages(PAGE_SIZE); - else + 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); - if (!page) + else + page = alloc_bootmem_low_pages(PAGE_SIZE); + if (!page) { + __sclp_vt220_cleanup(); return -ENOMEM; + } list_add_tail((struct list_head *) page, &sclp_vt220_empty); } return 0; @@ -662,14 +678,13 @@ static const struct tty_operations sclp_vt220_ops = { .flush_chars = sclp_vt220_flush_chars, .write_room = sclp_vt220_write_room, .chars_in_buffer = sclp_vt220_chars_in_buffer, - .flush_buffer = sclp_vt220_flush_buffer + .flush_buffer = sclp_vt220_flush_buffer, }; /* * Register driver with SCLP and Linux and initialize internal tty structures. */ -static int __init -sclp_vt220_tty_init(void) +static int __init sclp_vt220_tty_init(void) { struct tty_driver *driver; int rc; @@ -679,18 +694,15 @@ sclp_vt220_tty_init(void) driver = alloc_tty_driver(1); if (!driver) return -ENOMEM; - rc = __sclp_vt220_init(0); - if (rc) { - put_tty_driver(driver); - return rc; - } + rc = __sclp_vt220_init(); + if (rc) + goto out_driver; rc = sclp_register(&sclp_vt220_register); if (rc) { printk(KERN_ERR SCLP_VT220_PRINT_HEADER "could not register tty - " "sclp_register returned %d\n", rc); - put_tty_driver(driver); - return rc; + goto out_init; } driver->owner = THIS_MODULE; @@ -709,14 +721,20 @@ sclp_vt220_tty_init(void) printk(KERN_ERR SCLP_VT220_PRINT_HEADER "could not register tty - " "tty_register_driver returned %d\n", rc); - put_tty_driver(driver); - return rc; + goto out_sclp; } sclp_vt220_driver = driver; return 0; -} -module_init(sclp_vt220_tty_init); +out_sclp: + sclp_unregister(&sclp_vt220_register); +out_init: + __sclp_vt220_cleanup(); +out_driver: + put_tty_driver(driver); + return rc; +} +__initcall(sclp_vt220_tty_init); #ifdef CONFIG_SCLP_VT220_CONSOLE @@ -762,7 +780,7 @@ sclp_vt220_con_init(void) if (!CONSOLE_IS_SCLP) return 0; - rc = __sclp_vt220_init(1); + rc = __sclp_vt220_init(); if (rc) return rc; /* Attach linux console */ diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 3b52f5c1dbef..dddf8d62c153 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -188,7 +188,7 @@ struct tape_blk_data { struct tape_device * device; /* Block device request queue. */ - request_queue_t * request_queue; + struct request_queue * request_queue; spinlock_t request_queue_lock; /* Task to move entries from block request to CCS request queue. */ diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index e765875e8db2..80e7a537e7d2 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -131,10 +131,9 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) { struct tape_34xx_work *p; - if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL) + if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) return -ENOMEM; - memset(p, 0, sizeof(*p)); INIT_WORK(&p->work, tape_34xx_work_handler); p->device = tape_get_device_reference(device); diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index dd0ecaed592e..eeb92e2ed0cc 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -147,7 +147,7 @@ static void tapeblock_requeue(struct work_struct *work) { struct tape_blk_data * blkdat; struct tape_device * device; - request_queue_t * queue; + struct request_queue * queue; int nr_queued; struct request * req; struct list_head * l; @@ -194,7 +194,7 @@ tapeblock_requeue(struct work_struct *work) { * Tape request queue function. Called from ll_rw_blk.c */ static void -tapeblock_request_fn(request_queue_t *queue) +tapeblock_request_fn(struct request_queue *queue) { struct tape_device *device; diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index fce3dac5cb3e..2f419b0ea628 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2004,2005 IBM Corporation + * Copyright IBM Corp. 2004,2007 * Interface implementation for communication with the z/VM control program - * Author(s): Christian Borntraeger <cborntra@de.ibm.com> + * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> * * * z/VMs CP offers the possibility to issue commands via the diagnose code 8 @@ -22,9 +22,11 @@ #include "vmcp.h" MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Christian Borntraeger <cborntra@de.ibm.com>"); +MODULE_AUTHOR("Christian Borntraeger <borntraeger@de.ibm.com>"); MODULE_DESCRIPTION("z/VM CP interface"); +#define PRINTK_HEADER "vmcp: " + static debug_info_t *vmcp_debug; static int vmcp_open(struct inode *inode, struct file *file) @@ -40,7 +42,7 @@ static int vmcp_open(struct inode *inode, struct file *file) session->bufsize = PAGE_SIZE; session->response = NULL; session->resp_size = 0; - init_MUTEX(&session->mutex); + mutex_init(&session->mutex); file->private_data = session; return nonseekable_open(inode, file); } @@ -57,37 +59,37 @@ 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) +vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos) { size_t tocopy; struct vmcp_session *session; session = (struct vmcp_session *)file->private_data; - if (down_interruptible(&session->mutex)) + if (mutex_lock_interruptible(&session->mutex)) return -ERESTARTSYS; if (!session->response) { - up(&session->mutex); + mutex_unlock(&session->mutex); return 0; } if (*ppos > session->resp_size) { - up(&session->mutex); + mutex_unlock(&session->mutex); return 0; } tocopy = min(session->resp_size - (size_t) (*ppos), count); - tocopy = min(tocopy,session->bufsize - (size_t) (*ppos)); + tocopy = min(tocopy, session->bufsize - (size_t) (*ppos)); if (copy_to_user(buff, session->response + (*ppos), tocopy)) { - up(&session->mutex); + mutex_unlock(&session->mutex); return -EFAULT; } - up(&session->mutex); + mutex_unlock(&session->mutex); *ppos += tocopy; return tocopy; } static ssize_t -vmcp_write(struct file *file, const char __user * buff, size_t count, - loff_t * ppos) +vmcp_write(struct file *file, const char __user *buff, size_t count, + loff_t *ppos) { char *cmd; struct vmcp_session *session; @@ -103,24 +105,23 @@ vmcp_write(struct file *file, const char __user * buff, size_t count, } cmd[count] = '\0'; session = (struct vmcp_session *)file->private_data; - if (down_interruptible(&session->mutex)) { + if (mutex_lock_interruptible(&session->mutex)) { kfree(cmd); return -ERESTARTSYS; } if (!session->response) session->response = (char *)__get_free_pages(GFP_KERNEL - | __GFP_REPEAT | GFP_DMA, + | __GFP_REPEAT | GFP_DMA, get_order(session->bufsize)); if (!session->response) { - up(&session->mutex); + mutex_unlock(&session->mutex); kfree(cmd); return -ENOMEM; } debug_text_event(vmcp_debug, 1, cmd); - session->resp_size = cpcmd(cmd, session->response, - session->bufsize, - &session->resp_code); - up(&session->mutex); + session->resp_size = cpcmd(cmd, session->response, session->bufsize, + &session->resp_code); + mutex_unlock(&session->mutex); kfree(cmd); *ppos = 0; /* reset the file pointer after a command */ return count; @@ -145,12 +146,12 @@ static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) int temp; session = (struct vmcp_session *)file->private_data; - if (down_interruptible(&session->mutex)) + if (mutex_lock_interruptible(&session->mutex)) return -ERESTARTSYS; switch (cmd) { case VMCP_GETCODE: temp = session->resp_code; - up(&session->mutex); + mutex_unlock(&session->mutex); return put_user(temp, (int __user *)arg); case VMCP_SETBUF: free_pages((unsigned long)session->response, @@ -161,27 +162,26 @@ static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) session->bufsize = PAGE_SIZE; temp = -EINVAL; } - up(&session->mutex); + mutex_unlock(&session->mutex); return temp; case VMCP_GETSIZE: temp = session->resp_size; - up(&session->mutex); + mutex_unlock(&session->mutex); return put_user(temp, (int __user *)arg); default: - up(&session->mutex); + mutex_unlock(&session->mutex); return -ENOIOCTLCMD; } } static const struct file_operations vmcp_fops = { .owner = THIS_MODULE, - .open = &vmcp_open, - .release = &vmcp_release, - .read = &vmcp_read, - .llseek = &no_llseek, - .write = &vmcp_write, - .unlocked_ioctl = &vmcp_ioctl, - .compat_ioctl = &vmcp_ioctl + .open = vmcp_open, + .release = vmcp_release, + .read = vmcp_read, + .write = vmcp_write, + .unlocked_ioctl = vmcp_ioctl, + .compat_ioctl = vmcp_ioctl, }; static struct miscdevice vmcp_dev = { @@ -195,26 +195,38 @@ static int __init vmcp_init(void) int ret; if (!MACHINE_IS_VM) { - printk(KERN_WARNING - "z/VM CP interface is only available under z/VM\n"); + PRINT_WARN("z/VM CP interface is only available under z/VM\n"); return -ENODEV; } - ret = misc_register(&vmcp_dev); - if (!ret) - printk(KERN_INFO "z/VM CP interface loaded\n"); - else - printk(KERN_WARNING - "z/VM CP interface not loaded. Could not register misc device.\n"); vmcp_debug = debug_register("vmcp", 1, 1, 240); - debug_register_view(vmcp_debug, &debug_hex_ascii_view); - return ret; + if (!vmcp_debug) { + PRINT_ERR("z/VM CP interface not loaded. Could not register " + "debug feature\n"); + 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; } static void __exit vmcp_exit(void) { - WARN_ON(misc_deregister(&vmcp_dev) != 0); + misc_deregister(&vmcp_dev); debug_unregister(vmcp_debug); - printk(KERN_INFO "z/VM CP interface unloaded.\n"); + PRINT_INFO("z/VM CP interface unloaded.\n"); } module_init(vmcp_init); diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h index 8a5975f3dad7..6a993948e188 100644 --- a/drivers/s390/char/vmcp.h +++ b/drivers/s390/char/vmcp.h @@ -12,8 +12,8 @@ * The idea of this driver is based on cpint from Neale Ferguson */ -#include <asm/semaphore.h> #include <linux/ioctl.h> +#include <linux/mutex.h> #define VMCP_GETCODE _IOR(0x10, 1, int) #define VMCP_SETBUF _IOW(0x10, 2, int) @@ -26,5 +26,5 @@ struct vmcp_session { int resp_code; /* As we use copy_from/to_user, which might * * sleep and cannot use a spinlock */ - struct semaphore mutex; + struct mutex mutex; }; diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index a5a00e9ae4d0..12f7a4ce82c1 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -835,7 +835,7 @@ static void vmlogrdr_cleanup(void) } -static int vmlogrdr_init(void) +static int __init vmlogrdr_init(void) { int rc; int i; @@ -885,7 +885,7 @@ cleanup: } -static void vmlogrdr_exit(void) +static void __exit vmlogrdr_exit(void) { vmlogrdr_cleanup(); printk (KERN_INFO "vmlogrdr: driver unloaded\n"); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 4e711a985d59..3712ede16723 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -156,7 +156,7 @@ static int memcpy_real(void *dest, unsigned long src, size_t count) return rc; } -static int memcpy_real_user(__user void *dest, unsigned long src, size_t count) +static int memcpy_real_user(void __user *dest, unsigned long src, size_t count) { static char buf[4096]; int offs = 0, size; diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index ec0404874fad..bd5f16f80bf8 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -51,7 +51,7 @@ blacklist_range (range_action action, unsigned int from, unsigned int to, to = from; if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) { - printk (KERN_WARNING "Invalid blacklist range " + printk (KERN_WARNING "cio: Invalid blacklist range " "0.%x.%04x to 0.%x.%04x, skipping\n", ssid, from, ssid, to); return; @@ -119,7 +119,7 @@ blacklist_busid(char **str, int *id0, int *ssid, int *devno) return 0; confused: strsep(str, ",\n"); - printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav); + printk(KERN_WARNING "cio: Invalid cio_ignore parameter '%s'\n", sav); return 1; } @@ -166,22 +166,19 @@ blacklist_parse_parameters (char *str, range_action action) continue; } if (*str == '-') { - printk(KERN_WARNING "invalid cio_ignore " + printk(KERN_WARNING "cio: invalid cio_ignore " "parameter '%s'\n", strsep(&str, ",\n")); continue; } if ((from_id0 != to_id0) || (from_ssid != to_ssid)) { - printk(KERN_WARNING "invalid cio_ignore range " - "%x.%x.%04x-%x.%x.%04x\n", - from_id0, from_ssid, from, - to_id0, to_ssid, to); + printk(KERN_WARNING "cio: invalid cio_ignore " + "range %x.%x.%04x-%x.%x.%04x\n", + from_id0, from_ssid, from, + to_id0, to_ssid, to); continue; } - pr_debug("blacklist_setup: adding range " - "from %x.%x.%04x to %x.%x.%04x\n", - from_id0, from_ssid, from, to_id0, to_ssid, to); blacklist_range (ra, from, to, to_ssid); } } @@ -239,7 +236,7 @@ blacklist_parse_proc_parameters (char *buf) */ blacklist_parse_parameters (buf + 4, add); } else { - printk (KERN_WARNING "cio_ignore: Parse error; \n" + printk (KERN_WARNING "cio: cio_ignore: Parse error; \n" KERN_WARNING "try using 'free all|<devno-range>," "<devno-range>,...'\n" KERN_WARNING "or 'add <devno-range>," diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index e5ccda63e883..b0a18f5176aa 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -359,7 +359,6 @@ ccwgroup_probe (struct device *dev) if ((ret = device_create_file(dev, &dev_attr_online))) return ret; - pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; if (ret) device_remove_file(dev, &dev_attr_online); @@ -376,8 +375,6 @@ ccwgroup_remove (struct device *dev) gdev = to_ccwgroupdev(dev); gdrv = to_ccwgroupdrv(dev->driver); - pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); - device_remove_file(dev, &dev_attr_online); if (gdrv && gdrv->remove) diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index ac289e6eadfe..920dd71e6434 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -121,14 +121,8 @@ static int s390_vary_chpid(struct chp_id chpid, int on) CIO_TRACE_EVENT( 2, dbf_text); status = chp_get_status(chpid); - if (status < 0) { - printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", - chpid.cssid, chpid.id); - return -EINVAL; - } - if (!on && !status) { - printk(KERN_ERR "chpid %x.%02x is already offline\n", + printk(KERN_ERR "cio: chpid %x.%02x is already offline\n", chpid.cssid, chpid.id); return -EINVAL; } @@ -141,8 +135,9 @@ static int s390_vary_chpid(struct chp_id chpid, int on) /* * Channel measurement related functions */ -static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t chp_measurement_chars_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct channel_path *chp; unsigned int size; @@ -165,7 +160,6 @@ static struct bin_attribute chp_measurement_chars_attr = { .attr = { .name = "measurement_chars", .mode = S_IRUSR, - .owner = THIS_MODULE, }, .size = sizeof(struct cmg_chars), .read = chp_measurement_chars_read, @@ -193,8 +187,9 @@ static void chp_measurement_copy_block(struct cmg_entry *buf, } while (reference_buf.values[0] != buf->values[0]); } -static ssize_t chp_measurement_read(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t chp_measurement_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct channel_path *chp; struct channel_subsystem *css; @@ -217,7 +212,6 @@ static struct bin_attribute chp_measurement_attr = { .attr = { .name = "measurement", .mode = S_IRUSR, - .owner = THIS_MODULE, }, .size = sizeof(struct cmg_entry), .read = chp_measurement_read, @@ -421,21 +415,14 @@ int chp_new(struct chp_id chpid) if (ret) goto out_free; } else { - static int msg_done; - - if (!msg_done) { - printk(KERN_WARNING "cio: Channel measurements not " - "available, continuing.\n"); - msg_done = 1; - } chp->cmg = -1; } /* make it known to the system */ ret = device_register(&chp->dev); if (ret) { - printk(KERN_WARNING "%s: could not register %x.%02x\n", - __func__, chpid.cssid, chpid.id); + CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n", + chpid.cssid, chpid.id, ret); goto out_free; } ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index ea92ac4d6577..597c0c76a2ad 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -990,16 +990,20 @@ out: return ret; } -static int __init -chsc_alloc_sei_area(void) +int __init chsc_alloc_sei_area(void) { sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sei_page) - printk(KERN_WARNING"Can't allocate page for processing of " \ - "chsc machine checks!\n"); + CIO_MSG_EVENT(0, "Can't allocate page for processing of " + "chsc machine checks!\n"); return (sei_page ? 0 : -ENOMEM); } +void __init chsc_free_sei_area(void) +{ + kfree(sei_page); +} + int __init chsc_enable_facility(int operation_code) { @@ -1051,8 +1055,6 @@ chsc_enable_facility(int operation_code) return ret; } -subsys_initcall(chsc_alloc_sei_area); - struct css_general_char css_general_characteristics; struct css_chsc_char css_chsc_characteristics; @@ -1073,8 +1075,8 @@ chsc_determine_css_characteristics(void) scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scsc_area) { - printk(KERN_WARNING"cio: Was not able to determine available" \ - "CHSCs due to no memory.\n"); + CIO_MSG_EVENT(0, "Was not able to determine available" + "CHSCs due to no memory.\n"); return -ENOMEM; } @@ -1083,15 +1085,15 @@ chsc_determine_css_characteristics(void) result = chsc(scsc_area); if (result) { - printk(KERN_WARNING"cio: Was not able to determine " \ - "available CHSCs, cc=%i.\n", result); + CIO_MSG_EVENT(0, "Was not able to determine available CHSCs, " + "cc=%i.\n", result); result = -EIO; goto exit; } if (scsc_area->response.code != 1) { - printk(KERN_WARNING"cio: Was not able to determine " \ - "available CHSCs.\n"); + CIO_MSG_EVENT(0, "Was not able to determine " + "available CHSCs.\n"); result = -EIO; goto exit; } diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 2ad81d11cf7b..d1f5db1e69b9 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -79,6 +79,8 @@ 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); extern int chsc_enable_facility(int); struct channel_subsystem; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index ea1defba5693..f2708d65be5a 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -47,8 +47,8 @@ cio_setup (char *parm) else if (!strcmp (parm, "no")) cio_show_msg = 0; else - printk (KERN_ERR "cio_setup : invalid cio_msg parameter '%s'", - parm); + printk(KERN_ERR "cio: cio_setup: " + "invalid cio_msg parameter '%s'", parm); return 1; } @@ -80,7 +80,6 @@ cio_debug_init (void) goto out_unregister; debug_register_view (cio_debug_crw_id, &debug_sprintf_view); debug_set_level (cio_debug_crw_id, 2); - pr_debug("debugging initialized\n"); return 0; out_unregister: @@ -90,7 +89,7 @@ out_unregister: debug_unregister (cio_debug_trace_id); if (cio_debug_crw_id) debug_unregister (cio_debug_crw_id); - pr_debug("could not initialize debugging\n"); + printk(KERN_WARNING"cio: could not initialize debugging\n"); return -1; } @@ -568,7 +567,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) */ if (sch->st != 0) { CIO_DEBUG(KERN_INFO, 0, - "Subchannel 0.%x.%04x reports " + "cio: 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. */ @@ -601,7 +600,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, - "Detected device %04x on subchannel 0.%x.%04X" + "cio: 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, @@ -766,7 +765,7 @@ 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 "No ccw console found!\n"); + printk(KERN_WARNING "cio: No ccw console found!\n"); return -1; } return console_irq; diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index f88844adae1b..c9bf8989930f 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -23,6 +23,8 @@ extern debug_info_t *cio_debug_crw_id; static inline void CIO_HEX_EVENT(int level, void *data, int length) { + if (unlikely(!cio_debug_trace_id)) + return; while (length > 0) { debug_event(cio_debug_trace_id, level, data, length); length -= cio_debug_trace_id->buf_size; diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 28abd697be1a..34a796913b06 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -594,6 +594,9 @@ 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 { @@ -1185,12 +1188,12 @@ static ssize_t cmb_enable_store(struct device *dev, struct device_attribute *att case '0': ret = disable_cmf(cdev); if (ret) - printk(KERN_INFO "disable_cmf failed (%d)\n", ret); + dev_info(&cdev->dev, "disable_cmf failed (%d)\n", ret); break; case '1': ret = enable_cmf(cdev); if (ret && ret != -EBUSY) - printk(KERN_INFO "enable_cmf failed (%d)\n", ret); + dev_info(&cdev->dev, "enable_cmf failed (%d)\n", ret); break; } @@ -1279,26 +1282,19 @@ init_cmf(void) case CMF_BASIC: format_string = "basic"; cmbops = &cmbops_basic; - if (cmb_area.num_channels > 4096 || cmb_area.num_channels < 1) { - printk(KERN_ERR "Basic channel measurement facility" - " can only use 1 to 4096 devices\n" - KERN_ERR "when the cmf driver is built" - " as a loadable module\n"); - return 1; - } break; case CMF_EXTENDED: format_string = "extended"; cmbops = &cmbops_extended; break; default: - printk(KERN_ERR "Invalid format %d for channel " + printk(KERN_ERR "cio: Invalid format %d for channel " "measurement facility\n", format); return 1; } - printk(KERN_INFO "Channel measurement facility using %s format (%s)\n", - format_string, detect_string); + printk(KERN_INFO "cio: Channel measurement facility using %s " + "format (%s)\n", format_string, detect_string); return 0; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index dfca0ef139fd..5635e656c1a3 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -79,6 +79,7 @@ css_alloc_subchannel(struct subchannel_id schid) sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; ret = cio_modify(sch); if (ret) { + kfree(sch->lock); kfree(sch); return ERR_PTR(ret); } @@ -109,7 +110,7 @@ css_subchannel_release(struct device *dev) } } -int css_sch_device_register(struct subchannel *sch) +static int css_sch_device_register(struct subchannel *sch) { int ret; @@ -184,8 +185,8 @@ static int css_register_subchannel(struct subchannel *sch) /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { - printk (KERN_WARNING "%s: could not register %s\n", - __func__, sch->dev.bus_id); + CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n", + sch->schid.ssid, sch->schid.sch_no, ret); return ret; } return ret; @@ -371,15 +372,12 @@ static int __init slow_subchannel_init(void) spin_lock_init(&slow_subchannel_lock); slow_subchannel_set = idset_sch_new(); if (!slow_subchannel_set) { - printk(KERN_WARNING "cio: could not allocate slow subchannel " - "set\n"); + CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); return -ENOMEM; } return 0; } -subsys_initcall(slow_subchannel_init); - static void css_slow_path_func(struct work_struct *unused) { struct subchannel_id schid; @@ -425,8 +423,8 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data) struct subchannel *sch; int ret; - CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n", - schid.ssid, schid.sch_no); + CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", + schid.ssid, schid.sch_no); if (need_reprobe) return -EAGAIN; @@ -642,9 +640,20 @@ init_channel_subsystem (void) { int ret, i; - if (chsc_determine_css_characteristics() == 0) + 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) + goto out; + + ret = slow_subchannel_init(); + if (ret) + goto out; + if ((ret = bus_register(&css_bus_type))) goto out; @@ -710,6 +719,10 @@ out_unregister: out_bus: bus_unregister(&css_bus_type); out: + chsc_free_sei_area(); + kfree(slow_subchannel_set); + printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n", + ret); return ret; } diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ed7977531c3f..5d65e83ca66e 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -139,7 +139,6 @@ struct css_driver { */ extern struct bus_type css_bus_type; -extern int css_sch_device_register(struct subchannel *); extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6b264bdb5bfb..e44d92eac8e9 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -117,7 +117,10 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp, snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MODALIAS=%s", modalias_buf); - return ret; + if (ret) + return ret; + envp[i] = NULL; + return 0; } struct bus_type ccw_bus_type; @@ -272,7 +275,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf) struct ccw_device_id *id = &(cdev->id); int len; - len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1; + len = snprint_alias(buf, PAGE_SIZE, id, "\n"); return len > PAGE_SIZE ? PAGE_SIZE : len; } @@ -338,15 +341,20 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) rc = device_schedule_callback(&cdev->dev, ccw_device_remove_orphan_cb); if (rc) - dev_info(&cdev->dev, "Couldn't unregister orphan\n"); + CIO_MSG_EVENT(2, "Couldn't unregister orphan " + "0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); return; } /* Deregister subchannel, which will kill the ccw device. */ rc = device_schedule_callback(cdev->dev.parent, ccw_device_remove_sch_cb); if (rc) - dev_info(&cdev->dev, - "Couldn't unregister disconnected device\n"); + CIO_MSG_EVENT(2, "Couldn't unregister disconnected device " + "0.%x.%04x\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); } int @@ -379,8 +387,10 @@ ccw_device_set_offline(struct ccw_device *cdev) if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); else { - pr_debug("ccw_device_offline returned %d, device %s\n", - ret, cdev->dev.bus_id); + CIO_MSG_EVENT(2, "ccw_device_offline returned %d, " + "device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); cdev->online = 1; } return ret; @@ -402,8 +412,10 @@ ccw_device_set_online(struct ccw_device *cdev) if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); else { - pr_debug("ccw_device_online returned %d, device %s\n", - ret, cdev->dev.bus_id); + CIO_MSG_EVENT(2, "ccw_device_online returned %d, " + "device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); return ret; } if (cdev->private->state != DEV_STATE_ONLINE) @@ -417,9 +429,11 @@ ccw_device_set_online(struct ccw_device *cdev) spin_unlock_irq(cdev->ccwlock); if (ret == 0) wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); - else - pr_debug("ccw_device_offline returned %d, device %s\n", - ret, cdev->dev.bus_id); + else + CIO_MSG_EVENT(2, "ccw_device_offline returned %d, " + "device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); return (ret == 0) ? -ENODEV : ret; } @@ -439,9 +453,10 @@ static int online_store_recog_and_online(struct ccw_device *cdev) if (cdev->id.cu_type == 0) { ret = ccw_device_recognition(cdev); if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); + CIO_MSG_EVENT(0, "Couldn't start recognition " + "for device 0.%x.%04x (ret=%d)\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); return ret; } wait_event(cdev->private->wait_q, @@ -461,8 +476,8 @@ static void online_store_handle_online(struct ccw_device *cdev, int force) if (force && cdev->private->state == DEV_STATE_BOXED) { ret = ccw_device_stlck(cdev); if (ret) { - printk(KERN_WARNING"ccw_device_stlck for device %s " - "returned %d!\n", cdev->dev.bus_id, ret); + dev_warn(&cdev->dev, + "ccw_device_stlck returned %d!\n", ret); return; } if (cdev->id.cu_type == 0) @@ -893,8 +908,10 @@ io_subchannel_register(struct work_struct *work) ret = device_reprobe(&cdev->dev); if (ret) /* We can't do much here. */ - dev_info(&cdev->dev, "device_reprobe() returned" - " %d\n", ret); + CIO_MSG_EVENT(2, "device_reprobe() returned" + " %d for 0.%x.%04x\n", ret, + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); } goto out; } @@ -907,8 +924,9 @@ io_subchannel_register(struct work_struct *work) /* make it known to the system */ ret = ccw_device_register(cdev); if (ret) { - printk (KERN_WARNING "%s: could not register %s\n", - __func__, cdev->dev.bus_id); + CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno, ret); put_device(&cdev->dev); spin_lock_irqsave(sch->lock, flags); sch->dev.driver_data = NULL; @@ -1361,7 +1379,6 @@ ccw_device_remove (struct device *dev) struct ccw_driver *cdrv = cdev->drv; int ret; - pr_debug("removing device %s\n", cdev->dev.bus_id); if (cdrv->remove) cdrv->remove(cdev); if (cdev->online) { @@ -1374,8 +1391,10 @@ ccw_device_remove (struct device *dev) dev_fsm_final_state(cdev)); else //FIXME: we can't fail! - pr_debug("ccw_device_offline returned %d, device %s\n", - ret, cdev->dev.bus_id); + CIO_MSG_EVENT(2, "ccw_device_offline returned %d, " + "device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); } ccw_device_set_timeout(cdev, 0); cdev->drv = NULL; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 6bba80929577..8633dc537695 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -268,7 +268,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) switch (state) { case DEV_STATE_NOT_OPER: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : unknown device %04x on subchannel " + "cio: SenseID : unknown device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -293,7 +293,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) return; } /* Issue device info message. */ - CIO_DEBUG(KERN_INFO, 2, "SenseID : device 0.%x.%04x reports: " + CIO_DEBUG(KERN_INFO, 2, + "cio: SenseID : device 0.%x.%04x reports: " "CU Type/Mod = %04X/%02X, Dev Type/Mod = " "%04X/%02X\n", cdev->private->dev_id.ssid, @@ -303,7 +304,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : boxed device %04x on subchannel " + "cio: SenseID : boxed device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -388,7 +389,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_BOXED) CIO_DEBUG(KERN_WARNING, 2, - "Boxed device %04x on subchannel %04x\n", + "cio: Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->private->flags.donotify) { @@ -946,9 +947,10 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) /* Basic sense hasn't started. Try again. */ ccw_device_do_sense(cdev, irb); else { - printk(KERN_INFO "Huh? %s(%s): unsolicited " - "interrupt...\n", - __FUNCTION__, cdev->dev.bus_id); + CIO_MSG_EVENT(2, "Huh? 0.%x.%04x: unsolicited " + "interrupt during w4sense...\n", + cdev->private->dev_id.ssid, + cdev->private->dev_id.devno); if (cdev->handler) cdev->handler (cdev, 0, irb); } @@ -1215,8 +1217,8 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event) static void ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event) { - printk(KERN_EMERG "dev_jumptable[%i][%i] == NULL\n", - cdev->private->state, dev_event); + CIO_MSG_EVENT(0, "dev_jumptable[%i][%i] == NULL\n", + cdev->private->state, dev_event); BUG(); } diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c index 997f46874537..f232832f2b22 100644 --- a/drivers/s390/cio/device_id.c +++ b/drivers/s390/cio/device_id.c @@ -17,6 +17,7 @@ #include <asm/delay.h> #include <asm/cio.h> #include <asm/lowcore.h> +#include <asm/diag.h> #include "cio.h" #include "cio_debug.h" @@ -25,59 +26,6 @@ #include "ioasm.h" /* - * diag210 is used under VM to get information about a virtual device - */ -#ifdef CONFIG_64BIT -int -diag210(struct diag210 * addr) -{ - /* - * diag 210 needs its data below the 2GB border, so we - * use a static data area to be sure - */ - static struct diag210 diag210_tmp; - static DEFINE_SPINLOCK(diag210_lock); - unsigned long flags; - int ccode; - - spin_lock_irqsave(&diag210_lock, flags); - diag210_tmp = *addr; - - asm volatile( - " lhi %0,-1\n" - " sam31\n" - " diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" - "1: sam64\n" - EX_TABLE(0b,1b) - : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory"); - - *addr = diag210_tmp; - spin_unlock_irqrestore(&diag210_lock, flags); - - return ccode; -} -#else -int -diag210(struct diag210 * addr) -{ - int ccode; - - asm volatile( - " lhi %0,-1\n" - " diag %1,0,0x210\n" - "0: ipm %0\n" - " srl %0,28\n" - "1:\n" - EX_TABLE(0b,1b) - : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory"); - - return ccode; -} -#endif - -/* * Input : * devno - device number * ps - pointer to sense ID data area @@ -357,5 +305,3 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event) break; } } - -EXPORT_SYMBOL(diag210); diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index a5d263fb55ae..14eba854b155 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -288,253 +288,6 @@ ccw_device_get_path_mask(struct ccw_device *cdev) return sch->lpm; } -static void -ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb) -{ - if (!ip) - /* unsolicited interrupt */ - return; - - /* Abuse intparm for error reporting. */ - if (IS_ERR(irb)) - cdev->private->intparm = -EIO; - else if (irb->scsw.cc == 1) - /* Retry for deferred condition code. */ - cdev->private->intparm = -EAGAIN; - else if ((irb->scsw.dstat != - (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || - (irb->scsw.cstat != 0)) { - /* - * We didn't get channel end / device end. Check if path - * verification has been started; we can retry after it has - * finished. We also retry unit checks except for command reject - * or intervention required. Also check for long busy - * conditions. - */ - if (cdev->private->flags.doverify || - cdev->private->state == DEV_STATE_VERIFY) - cdev->private->intparm = -EAGAIN; - else if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && - !(irb->ecw[0] & - (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ))) - cdev->private->intparm = -EAGAIN; - else if ((irb->scsw.dstat & DEV_STAT_ATTENTION) && - (irb->scsw.dstat & DEV_STAT_DEV_END) && - (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP)) - cdev->private->intparm = -EAGAIN; - else - cdev->private->intparm = -EIO; - - } else - cdev->private->intparm = 0; - wake_up(&cdev->private->wait_q); -} - -static int -__ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, __u8 lpm) -{ - int ret; - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); - do { - ccw_device_set_timeout(cdev, 60 * HZ); - ret = cio_start (sch, ccw, lpm); - if (ret != 0) - ccw_device_set_timeout(cdev, 0); - if (ret == -EBUSY) { - /* Try again later. */ - spin_unlock_irq(sch->lock); - msleep(10); - spin_lock_irq(sch->lock); - continue; - } - if (ret != 0) - /* Non-retryable error. */ - break; - /* Wait for end of request. */ - cdev->private->intparm = magic; - spin_unlock_irq(sch->lock); - wait_event(cdev->private->wait_q, - (cdev->private->intparm == -EIO) || - (cdev->private->intparm == -EAGAIN) || - (cdev->private->intparm == 0)); - spin_lock_irq(sch->lock); - /* Check at least for channel end / device end */ - if (cdev->private->intparm == -EIO) { - /* Non-retryable error. */ - ret = -EIO; - break; - } - if (cdev->private->intparm == 0) - /* Success. */ - break; - /* Try again later. */ - spin_unlock_irq(sch->lock); - msleep(10); - spin_lock_irq(sch->lock); - } while (1); - - return ret; -} - -/** - * read_dev_chars() - read device characteristics - * @param cdev target ccw device - * @param buffer pointer to buffer for rdc data - * @param length size of rdc data - * @returns 0 for success, negative error value on failure - * - * Context: - * called for online device, lock not held - **/ -int -read_dev_chars (struct ccw_device *cdev, void **buffer, int length) -{ - void (*handler)(struct ccw_device *, unsigned long, struct irb *); - struct subchannel *sch; - int ret; - struct ccw1 *rdc_ccw; - - if (!cdev) - return -ENODEV; - if (!buffer || !length) - return -EINVAL; - sch = to_subchannel(cdev->dev.parent); - - CIO_TRACE_EVENT (4, "rddevch"); - CIO_TRACE_EVENT (4, sch->dev.bus_id); - - rdc_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); - if (!rdc_ccw) - return -ENOMEM; - rdc_ccw->cmd_code = CCW_CMD_RDC; - rdc_ccw->count = length; - rdc_ccw->flags = CCW_FLAG_SLI; - ret = set_normalized_cda (rdc_ccw, (*buffer)); - if (ret != 0) { - kfree(rdc_ccw); - return ret; - } - - spin_lock_irq(sch->lock); - /* Save interrupt handler. */ - handler = cdev->handler; - /* Temporarily install own handler. */ - cdev->handler = ccw_device_wake_up; - if (cdev->private->state != DEV_STATE_ONLINE) - ret = -ENODEV; - else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) && - !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) || - cdev->private->flags.doverify) - ret = -EBUSY; - else - /* 0x00D9C4C3 == ebcdic "RDC" */ - ret = __ccw_device_retry_loop(cdev, rdc_ccw, 0x00D9C4C3, 0); - - /* Restore interrupt handler. */ - cdev->handler = handler; - spin_unlock_irq(sch->lock); - - clear_normalized_cda (rdc_ccw); - kfree(rdc_ccw); - - return ret; -} - -/* - * Read Configuration data using path mask - */ -int -read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lpm) -{ - void (*handler)(struct ccw_device *, unsigned long, struct irb *); - struct subchannel *sch; - struct ciw *ciw; - char *rcd_buf; - int ret; - struct ccw1 *rcd_ccw; - - if (!cdev) - return -ENODEV; - if (!buffer || !length) - return -EINVAL; - sch = to_subchannel(cdev->dev.parent); - - CIO_TRACE_EVENT (4, "rdconf"); - CIO_TRACE_EVENT (4, sch->dev.bus_id); - - /* - * scan for RCD command in extended SenseID data - */ - ciw = ccw_device_get_ciw(cdev, CIW_TYPE_RCD); - if (!ciw || ciw->cmd == 0) - return -EOPNOTSUPP; - - /* Adjust requested path mask to excluded varied off paths. */ - if (lpm) { - lpm &= sch->opm; - if (lpm == 0) - return -EACCES; - } - - rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); - if (!rcd_ccw) - return -ENOMEM; - rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); - if (!rcd_buf) { - kfree(rcd_ccw); - return -ENOMEM; - } - rcd_ccw->cmd_code = ciw->cmd; - rcd_ccw->cda = (__u32) __pa (rcd_buf); - rcd_ccw->count = ciw->count; - rcd_ccw->flags = CCW_FLAG_SLI; - - spin_lock_irq(sch->lock); - /* Save interrupt handler. */ - handler = cdev->handler; - /* Temporarily install own handler. */ - cdev->handler = ccw_device_wake_up; - if (cdev->private->state != DEV_STATE_ONLINE) - ret = -ENODEV; - else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) && - !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) || - cdev->private->flags.doverify) - ret = -EBUSY; - else - /* 0x00D9C3C4 == ebcdic "RCD" */ - ret = __ccw_device_retry_loop(cdev, rcd_ccw, 0x00D9C3C4, lpm); - - /* Restore interrupt handler. */ - cdev->handler = handler; - spin_unlock_irq(sch->lock); - - /* - * on success we update the user input parms - */ - if (ret) { - kfree (rcd_buf); - *buffer = NULL; - *length = 0; - } else { - *length = ciw->count; - *buffer = rcd_buf; - } - kfree(rcd_ccw); - - return ret; -} - -/* - * Read Configuration data - */ -int -read_conf_data (struct ccw_device *cdev, void **buffer, int *length) -{ - return read_conf_data_lpm (cdev, buffer, length, 0); -} - /* * Try to break the lock on a boxed device. */ @@ -635,12 +388,6 @@ _ccw_device_get_subchannel_number(struct ccw_device *cdev) return cdev->private->schid.sch_no; } -int -_ccw_device_get_device_number(struct ccw_device *cdev) -{ - return cdev->private->dev_id.devno; -} - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_options_mask); @@ -655,9 +402,5 @@ EXPORT_SYMBOL(ccw_device_start_timeout_key); EXPORT_SYMBOL(ccw_device_start_key); EXPORT_SYMBOL(ccw_device_get_ciw); EXPORT_SYMBOL(ccw_device_get_path_mask); -EXPORT_SYMBOL(read_conf_data); -EXPORT_SYMBOL(read_dev_chars); EXPORT_SYMBOL(_ccw_device_get_subchannel_number); -EXPORT_SYMBOL(_ccw_device_get_device_number); EXPORT_SYMBOL_GPL(ccw_device_get_chp_desc); -EXPORT_SYMBOL_GPL(read_conf_data_lpm); diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index e70aeb7a3781..d8d479876ec7 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -81,6 +81,7 @@ static __u32 volatile spare_indicator; static atomic_t spare_indicator_usecount; #define QDIO_MEMPOOL_SCSSC_ELEMENTS 2 static mempool_t *qdio_mempool_scssc; +static struct kmem_cache *qdio_q_cache; static debug_info_t *qdio_dbf_setup; static debug_info_t *qdio_dbf_sbal; @@ -166,9 +167,9 @@ qdio_check_ccq(struct qdio_q *q, unsigned int ccq) { char dbf_text[15]; - if (ccq == 0 || ccq == 32 || ccq == 96) + if (ccq == 0 || ccq == 32) return 0; - if (ccq == 97) + if (ccq == 96 || ccq == 97) return 1; /*notify devices immediately*/ sprintf(dbf_text,"%d", ccq); @@ -194,6 +195,8 @@ qdio_do_eqbs(struct qdio_q *q, unsigned char *state, again: ccq = do_eqbs(irq->sch_token, state, q_no, start, cnt); rc = qdio_check_ccq(q, ccq); + if ((ccq == 96) && (tmp_cnt != *cnt)) + rc = 0; if (rc == 1) { QDIO_DBF_TEXT5(1,trace,"eqAGAIN"); goto again; @@ -739,7 +742,8 @@ qdio_get_outbound_buffer_frontier(struct qdio_q *q) first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used), (QDIO_MAX_BUFFERS_PER_Q-1)); - if ((!q->is_iqdio_q)&&(!q->hydra_gives_outbound_pcis)) + if (((!q->is_iqdio_q) && (!q->hydra_gives_outbound_pcis)) || + (q->queue_type == QDIO_IQDIO_QFMT_ASYNCH)) SYNC_MEMORY; check_next: @@ -1617,23 +1621,21 @@ static void qdio_release_irq_memory(struct qdio_irq *irq_ptr) { int i; + struct qdio_q *q; - for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) { - if (!irq_ptr->input_qs[i]) - goto next; - - kfree(irq_ptr->input_qs[i]->slib); - kfree(irq_ptr->input_qs[i]); - -next: - if (!irq_ptr->output_qs[i]) - continue; - - kfree(irq_ptr->output_qs[i]->slib); - kfree(irq_ptr->output_qs[i]); - + for (i = 0; i < QDIO_MAX_QUEUES_PER_IRQ; i++) { + q = irq_ptr->input_qs[i]; + if (q) { + free_page((unsigned long) q->slib); + kmem_cache_free(qdio_q_cache, q); + } + q = irq_ptr->output_qs[i]; + if (q) { + free_page((unsigned long) q->slib); + kmem_cache_free(qdio_q_cache, q); + } } - kfree(irq_ptr->qdr); + free_page((unsigned long) irq_ptr->qdr); free_page((unsigned long) irq_ptr); } @@ -1680,44 +1682,35 @@ qdio_alloc_qs(struct qdio_irq *irq_ptr, { int i; struct qdio_q *q; - int result=-ENOMEM; - for (i=0;i<no_input_qs;i++) { - q = kzalloc(sizeof(struct qdio_q), GFP_KERNEL); - - if (!q) { - QDIO_PRINT_ERR("kmalloc of q failed!\n"); - goto out; - } + for (i = 0; i < no_input_qs; i++) { + q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL); + if (!q) + return -ENOMEM; + memset(q, 0, sizeof(*q)); - q->slib = kmalloc(PAGE_SIZE, GFP_KERNEL); + q->slib = (struct slib *) __get_free_page(GFP_KERNEL); if (!q->slib) { - QDIO_PRINT_ERR("kmalloc of slib failed!\n"); - goto out; + kmem_cache_free(qdio_q_cache, q); + return -ENOMEM; } - irq_ptr->input_qs[i]=q; } - for (i=0;i<no_output_qs;i++) { - q = kzalloc(sizeof(struct qdio_q), GFP_KERNEL); - - if (!q) { - goto out; - } + for (i = 0; i < no_output_qs; i++) { + q = kmem_cache_alloc(qdio_q_cache, GFP_KERNEL); + if (!q) + return -ENOMEM; + memset(q, 0, sizeof(*q)); - q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL); + q->slib = (struct slib *) __get_free_page(GFP_KERNEL); if (!q->slib) { - QDIO_PRINT_ERR("kmalloc of slib failed!\n"); - goto out; + kmem_cache_free(qdio_q_cache, q); + return -ENOMEM; } - irq_ptr->output_qs[i]=q; } - - result=0; -out: - return result; + return 0; } static void @@ -2306,8 +2299,8 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr) if (!ssqd_area) { QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ "SIGAs for sch x%x.\n", irq_ptr->schid.sch_no); - irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || - CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + irq_ptr->qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | + CHSC_FLAG_SIGA_OUTPUT_NECESSARY | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; irq_ptr->sch_token = 0; @@ -2328,8 +2321,8 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr) QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \ "SIGAs for sch 0.%x.%x.\n", result, irq_ptr->schid.ssid, irq_ptr->schid.sch_no); - qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || - CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | + CHSC_FLAG_SIGA_OUTPUT_NECESSARY | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; goto out; @@ -2340,8 +2333,8 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr) "is 0x%x. Using all SIGAs for sch 0.%x.%x.\n", ssqd_area->response.code, irq_ptr->schid.ssid, irq_ptr->schid.sch_no); - qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY || - CHSC_FLAG_SIGA_OUTPUT_NECESSARY || + qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY | + CHSC_FLAG_SIGA_OUTPUT_NECESSARY | CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */ irq_ptr->is_qebsm = 0; goto out; @@ -2985,17 +2978,17 @@ qdio_allocate(struct qdio_initialize *init_data) QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); if (!irq_ptr) { - QDIO_PRINT_ERR("kmalloc of irq_ptr failed!\n"); + QDIO_PRINT_ERR("allocation of irq_ptr failed!\n"); return -ENOMEM; } init_MUTEX(&irq_ptr->setting_up_sema); /* QDR must be in DMA area since CCW data address is only 32 bit */ - irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); + irq_ptr->qdr = (struct qdr *) __get_free_page(GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { free_page((unsigned long) irq_ptr); - QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); + QDIO_PRINT_ERR("allocation of irq_ptr->qdr failed!\n"); return -ENOMEM; } QDIO_DBF_TEXT0(0,setup,"qdr:"); @@ -3004,6 +2997,7 @@ qdio_allocate(struct qdio_initialize *init_data) if (qdio_alloc_qs(irq_ptr, init_data->no_input_qs, init_data->no_output_qs)) { + QDIO_PRINT_ERR("queue allocation failed!\n"); qdio_release_irq_memory(irq_ptr); return -ENOMEM; } @@ -3895,9 +3889,19 @@ init_QDIO(void) if (res) return res; + qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q), + 256, 0, NULL); + if (!qdio_q_cache) { + qdio_release_qdio_memory(); + return -ENOMEM; + } + res = qdio_register_dbf_views(); - if (res) + if (res) { + kmem_cache_destroy(qdio_q_cache); + qdio_release_qdio_memory(); return res; + } QDIO_DBF_TEXT0(0,setup,"initQDIO"); res = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); @@ -3929,6 +3933,7 @@ cleanup_QDIO(void) qdio_release_qdio_memory(); qdio_unregister_dbf_views(); mempool_destroy(qdio_mempool_scssc); + kmem_cache_destroy(qdio_q_cache); bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); printk("qdio: %s: module removed\n",version); } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5aac0ec36368..90bd22014513 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -43,6 +43,7 @@ static void ap_poll_all(unsigned long); static void ap_poll_timeout(unsigned long); static int ap_poll_thread_start(void); static void ap_poll_thread_stop(void); +static void ap_request_timeout(unsigned long); /** * Module description. @@ -189,6 +190,7 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) case AP_RESPONSE_NORMAL: return 0; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: return -EBUSY; default: /* Device is gone. */ return -ENODEV; @@ -252,6 +254,8 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) if (status.queue_empty) return -ENOENT; return -EBUSY; + case AP_RESPONSE_RESET_IN_PROGRESS: + return -EBUSY; default: return -ENODEV; } @@ -326,11 +330,12 @@ static int ap_init_queue(ap_qid_t qid) i = AP_MAX_RESET; /* return with -ENODEV */ break; case AP_RESPONSE_RESET_IN_PROGRESS: + rc = -EBUSY; case AP_RESPONSE_BUSY: default: break; } - if (rc != -ENODEV) + if (rc != -ENODEV && rc != -EBUSY) break; if (i < AP_MAX_RESET - 1) { udelay(5); @@ -341,6 +346,40 @@ static int ap_init_queue(ap_qid_t qid) } /** + * Arm request timeout if a AP device was idle and a new request is submitted. + */ +static void ap_increase_queue_count(struct ap_device *ap_dev) +{ + int timeout = ap_dev->drv->request_timeout; + + ap_dev->queue_count++; + if (ap_dev->queue_count == 1) { + mod_timer(&ap_dev->timeout, jiffies + timeout); + ap_dev->reset = AP_RESET_ARMED; + } +} + +/** + * AP device is still alive, re-schedule request timeout if there are still + * pending requests. + */ +static void ap_decrease_queue_count(struct ap_device *ap_dev) +{ + int timeout = ap_dev->drv->request_timeout; + + ap_dev->queue_count--; + if (ap_dev->queue_count > 0) + mod_timer(&ap_dev->timeout, jiffies + timeout); + else + /** + * The timeout timer should to be disabled now - since + * del_timer_sync() is very expensive, we just tell via the + * reset flag to ignore the pending timeout timer. + */ + ap_dev->reset = AP_RESET_IGNORE; +} + +/** * AP device related attributes. */ static ssize_t ap_hwtype_show(struct device *dev, @@ -498,6 +537,7 @@ static int ap_device_remove(struct device *dev) struct ap_driver *ap_drv = ap_dev->drv; ap_flush_queue(ap_dev); + del_timer_sync(&ap_dev->timeout); if (ap_drv->remove) ap_drv->remove(ap_dev); spin_lock_bh(&ap_device_lock); @@ -759,17 +799,21 @@ static void ap_scan_bus(struct work_struct *unused) __ap_scan_bus); rc = ap_query_queue(qid, &queue_depth, &device_type); if (dev) { + if (rc == -EBUSY) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AP_RESET_TIMEOUT); + rc = ap_query_queue(qid, &queue_depth, + &device_type); + } ap_dev = to_ap_dev(dev); spin_lock_bh(&ap_dev->lock); if (rc || ap_dev->unregistered) { spin_unlock_bh(&ap_dev->lock); - put_device(dev); device_unregister(dev); + put_device(dev); continue; - } else - spin_unlock_bh(&ap_dev->lock); - } - if (dev) { + } + spin_unlock_bh(&ap_dev->lock); put_device(dev); continue; } @@ -788,6 +832,8 @@ static void ap_scan_bus(struct work_struct *unused) INIT_LIST_HEAD(&ap_dev->pendingq); INIT_LIST_HEAD(&ap_dev->requestq); INIT_LIST_HEAD(&ap_dev->list); + setup_timer(&ap_dev->timeout, ap_request_timeout, + (unsigned long) ap_dev); if (device_type == 0) ap_probe_device_type(ap_dev); else @@ -853,7 +899,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_dec(&ap_poll_requests); - ap_dev->queue_count--; + ap_decrease_queue_count(ap_dev); list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { if (ap_msg->psmid != ap_dev->reply->psmid) continue; @@ -904,7 +950,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) switch (status.response_code) { case AP_RESPONSE_NORMAL: atomic_inc(&ap_poll_requests); - ap_dev->queue_count++; + ap_increase_queue_count(ap_dev); list_move_tail(&ap_msg->list, &ap_dev->pendingq); ap_dev->requestq_count--; ap_dev->pendingq_count++; @@ -914,6 +960,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) *flags |= 2; break; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: *flags |= 2; break; case AP_RESPONSE_MESSAGE_TOO_BIG: @@ -960,10 +1007,11 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms list_add_tail(&ap_msg->list, &ap_dev->pendingq); atomic_inc(&ap_poll_requests); ap_dev->pendingq_count++; - ap_dev->queue_count++; + ap_increase_queue_count(ap_dev); ap_dev->total_request_count++; break; case AP_RESPONSE_Q_FULL: + case AP_RESPONSE_RESET_IN_PROGRESS: list_add_tail(&ap_msg->list, &ap_dev->requestq); ap_dev->requestq_count++; ap_dev->total_request_count++; @@ -1046,6 +1094,25 @@ static void ap_poll_timeout(unsigned long unused) } /** + * Reset a not responding AP device and move all requests from the + * pending queue to the request queue. + */ +static void ap_reset(struct ap_device *ap_dev) +{ + int rc; + + ap_dev->reset = AP_RESET_IGNORE; + atomic_sub(ap_dev->queue_count, &ap_poll_requests); + ap_dev->queue_count = 0; + list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); + ap_dev->requestq_count += ap_dev->pendingq_count; + ap_dev->pendingq_count = 0; + rc = ap_init_queue(ap_dev->qid); + if (rc == -ENODEV) + ap_dev->unregistered = 1; +} + +/** * Poll all AP devices on the bus in a round robin fashion. Continue * polling until bit 2^0 of the control flags is not set. If bit 2^1 * of the control flags has been set arm the poll timer. @@ -1056,6 +1123,8 @@ static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) if (!ap_dev->unregistered) { if (ap_poll_queue(ap_dev, flags)) ap_dev->unregistered = 1; + if (ap_dev->reset == AP_RESET_DO) + ap_reset(ap_dev); } spin_unlock(&ap_dev->lock); return 0; @@ -1147,6 +1216,17 @@ static void ap_poll_thread_stop(void) mutex_unlock(&ap_poll_thread_mutex); } +/** + * Handling of request timeouts + */ +static void ap_request_timeout(unsigned long data) +{ + struct ap_device *ap_dev = (struct ap_device *) data; + + if (ap_dev->reset == AP_RESET_ARMED) + ap_dev->reset = AP_RESET_DO; +} + static void ap_reset_domain(void) { int i; diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 008559ea742b..87c2d6442875 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -33,6 +33,7 @@ #define AP_DEVICES 64 /* Number of AP devices. */ #define AP_DOMAINS 16 /* Number of AP domains. */ #define AP_MAX_RESET 90 /* Maximum number of resets. */ +#define AP_RESET_TIMEOUT (HZ/2) /* Time in ticks for reset timeouts. */ #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ #define AP_POLL_TIME 1 /* Time in ticks between receive polls. */ @@ -83,6 +84,13 @@ struct ap_queue_status { #define AP_DEVICE_TYPE_CEX2A 6 #define AP_DEVICE_TYPE_CEX2C 7 +/** + * AP reset flag states + */ +#define AP_RESET_IGNORE 0 /* request timeout will be ignored */ +#define AP_RESET_ARMED 1 /* request timeout timer is active */ +#define AP_RESET_DO 2 /* AP reset required */ + struct ap_device; struct ap_message; @@ -95,6 +103,7 @@ struct ap_driver { /* receive is called from tasklet context */ void (*receive)(struct ap_device *, struct ap_message *, struct ap_message *); + int request_timeout; /* request timeout in jiffies */ }; #define to_ap_drv(x) container_of((x), struct ap_driver, driver) @@ -112,6 +121,8 @@ struct ap_device { int queue_depth; /* AP queue depth.*/ int device_type; /* AP device type. */ int unregistered; /* marks AP device as unregistered */ + struct timer_list timeout; /* Timer for request timeouts. */ + int reset; /* Reset required after req. timeout. */ int queue_count; /* # messages currently on AP queue. */ diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 5bb13a9d0898..08657f604b8c 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -70,6 +70,7 @@ static struct ap_driver zcrypt_cex2a_driver = { .remove = zcrypt_cex2a_remove, .receive = zcrypt_cex2a_receive, .ids = zcrypt_cex2a_ids, + .request_timeout = CEX2A_CLEANUP_TIME, }; /** @@ -306,18 +307,13 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, CEX2A_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; @@ -348,18 +344,13 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, CEX2A_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 818ffe05ac00..6e93b4751782 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -70,6 +70,7 @@ static struct ap_driver zcrypt_pcica_driver = { .remove = zcrypt_pcica_remove, .receive = zcrypt_pcica_receive, .ids = zcrypt_pcica_ids, + .request_timeout = PCICA_CLEANUP_TIME, }; /** @@ -290,18 +291,13 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICA_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; @@ -332,18 +328,13 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICA_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: kfree(ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index f295a403b29a..d6d59bf9ac38 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -82,6 +82,7 @@ static struct ap_driver zcrypt_pcicc_driver = { .remove = zcrypt_pcicc_remove, .receive = zcrypt_pcicc_receive, .ids = zcrypt_pcicc_ids, + .request_timeout = PCICC_CLEANUP_TIME, }; /** @@ -501,18 +502,13 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -544,18 +540,13 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &work, PCICC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&work); + if (rc == 0) rc = convert_response(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 252443b6bd1b..64948788d301 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -93,6 +93,7 @@ static struct ap_driver zcrypt_pcixcc_driver = { .remove = zcrypt_pcixcc_remove, .receive = zcrypt_pcixcc_receive, .ids = zcrypt_pcixcc_ids, + .request_timeout = PCIXCC_CLEANUP_TIME, }; /** @@ -641,18 +642,13 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_ica(zdev, &ap_msg, mex->outputdata, mex->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -685,18 +681,13 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_ica(zdev, &ap_msg, crt->outputdata, crt->outputdatalength); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: free_page((unsigned long) ap_msg.message); return rc; @@ -729,17 +720,12 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev, goto out_free; init_completion(&resp_type.work); ap_queue_message(zdev->ap_dev, &ap_msg); - rc = wait_for_completion_interruptible_timeout( - &resp_type.work, PCIXCC_CLEANUP_TIME); - if (rc > 0) + rc = wait_for_completion_interruptible(&resp_type.work); + if (rc == 0) rc = convert_response_xcrb(zdev, &ap_msg, xcRB); - else { - /* Signal pending or message timed out. */ + else + /* Signal pending. */ ap_cancel_message(zdev->ap_dev, &ap_msg); - if (rc == 0) - /* Message timed out. */ - rc = -ETIME; - } out_free: memset(ap_msg.message, 0x0, ap_msg.length); kfree(ap_msg.message); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 348bb7b82771..023455a0b34a 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -317,8 +317,8 @@ claw_probe(struct ccwgroup_device *cgdev) CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM); return -ENOMEM; } - privptr->p_mtc_envelope= kmalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL); - privptr->p_env = kmalloc(sizeof(struct claw_env), GFP_KERNEL); + privptr->p_mtc_envelope= kzalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL); + privptr->p_env = kzalloc(sizeof(struct claw_env), GFP_KERNEL); if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) { probe_error(cgdev); put_device(&cgdev->dev); @@ -327,8 +327,6 @@ claw_probe(struct ccwgroup_device *cgdev) CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM); return -ENOMEM; } - memset(privptr->p_mtc_envelope, 0x00, MAX_ENVELOPE_SIZE); - memset(privptr->p_env, 0x00, sizeof(struct claw_env)); memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8); memcpy(privptr->p_env->host_name,WS_NAME_NOT_DEF,8); memcpy(privptr->p_env->api_type,WS_NAME_NOT_DEF,8); @@ -3924,7 +3922,7 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr) snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", cdev->dev.bus_id); ccw_device_get_id(cdev, &dev_id); p_ch->devno = dev_id.devno; - if ((p_ch->irb = kmalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) { + if ((p_ch->irb = kzalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) { printk(KERN_WARNING "%s Out of memory in %s for irb\n", p_ch->id,__FUNCTION__); #ifdef FUNCTRACE @@ -3933,7 +3931,6 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr) #endif return -ENOMEM; } - memset(p_ch->irb, 0, sizeof (struct irb)); #ifdef FUNCTRACE printk(KERN_INFO "%s:%s Exit on line %d\n", cdev->dev.bus_id,__FUNCTION__,__LINE__); diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c index b20fd0681733..92e8a37b5022 100644 --- a/drivers/s390/net/ctcmain.c +++ b/drivers/s390/net/ctcmain.c @@ -674,7 +674,7 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) int first = 1; int i; unsigned long duration; - struct timespec done_stamp = xtime; + struct timespec done_stamp = current_kernel_time(); DBF_TEXT(trace, 4, __FUNCTION__); @@ -730,7 +730,7 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) spin_unlock(&ch->collect_lock); ch->ccw[1].count = ch->trans_skb->len; fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - ch->prof.send_stamp = xtime; + ch->prof.send_stamp = current_kernel_time(); rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); ch->prof.doios_multi++; @@ -2281,7 +2281,7 @@ transmit_skb(struct channel *ch, struct sk_buff *skb) fsm_newstate(ch->fsm, CH_STATE_TX); fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - ch->prof.send_stamp = xtime; + ch->prof.send_stamp = current_kernel_time(); rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], (unsigned long) ch, 0xff, 0); spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 3d28e1a5bf79..268889474339 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -753,7 +753,7 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) header.next = 0; memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); - conn->prof.send_stamp = xtime; + conn->prof.send_stamp = current_kernel_time(); txmsg.class = 0; txmsg.tag = 0; rc = iucv_message_send(conn->path, &txmsg, 0, 0, @@ -1185,7 +1185,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); fsm_newstate(conn->fsm, CONN_STATE_TX); - conn->prof.send_stamp = xtime; + conn->prof.send_stamp = current_kernel_time(); msg.tag = 1; msg.class = 0; diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index b34eb82edd98..6d4959807abc 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h @@ -211,6 +211,10 @@ struct qeth_perf_stats { /* initial values when measuring starts */ unsigned long initial_rx_packets; unsigned long initial_tx_packets; + /* inbound scatter gather data */ + unsigned int sg_skbs_rx; + unsigned int sg_frags_rx; + unsigned int sg_alloc_page_rx; }; /* Routing stuff */ @@ -341,6 +345,9 @@ qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, enum qeth_ipa_funcs func) #define QETH_IP_HEADER_SIZE 40 +/* large receive scatter gather copy break */ +#define QETH_RX_SG_CB (PAGE_SIZE >> 1) + struct qeth_hdr_layer3 { __u8 id; __u8 flags; @@ -771,6 +778,7 @@ struct qeth_card_options { int layer2; enum qeth_large_send_types large_send; int performance_stats; + int rx_sg_cb; }; /* @@ -828,6 +836,7 @@ struct qeth_card { int (*orig_hard_header)(struct sk_buff *,struct net_device *, unsigned short,void *,void *,unsigned); struct qeth_osn_info osn_info; + atomic_t force_alloc_skb; }; struct qeth_card_list_struct { @@ -1169,9 +1178,9 @@ qeth_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr, char *buf) { if (proto == QETH_PROT_IPV4) - return qeth_ipaddr4_to_string(addr, buf); + qeth_ipaddr4_to_string(addr, buf); else if (proto == QETH_PROT_IPV6) - return qeth_ipaddr6_to_string(addr, buf); + qeth_ipaddr6_to_string(addr, buf); } static inline int diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 86b0c44165c1..f3e6fbeb2123 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -561,7 +561,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev) } static int -qeth_wait_for_threads(struct qeth_card *card, unsigned long threads); +qeth_threads_running(struct qeth_card *card, unsigned long threads); static void @@ -576,8 +576,7 @@ qeth_remove_device(struct ccwgroup_device *cgdev) if (!card) return; - if (qeth_wait_for_threads(card, 0xffffffff)) - return; + wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); if (cgdev->state == CCWGROUP_ONLINE){ card->use_hard_stop = 1; @@ -1054,6 +1053,7 @@ qeth_set_intial_options(struct qeth_card *card) else card->options.layer2 = 0; card->options.performance_stats = 0; + card->options.rx_sg_cb = QETH_RX_SG_CB; } /** @@ -1541,16 +1541,21 @@ qeth_idx_write_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) card = CARD_FROM_CDEV(channel->ccwdev); if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { - PRINT_ERR("IDX_ACTIVATE on write channel device %s: negative " - "reply\n", CARD_WDEV_ID(card)); + if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19) + PRINT_ERR("IDX_ACTIVATE on write channel device %s: " + "adapter exclusively used by another host\n", + CARD_WDEV_ID(card)); + else + PRINT_ERR("IDX_ACTIVATE on write channel device %s: " + "negative reply\n", CARD_WDEV_ID(card)); goto out; } memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) { PRINT_WARN("IDX_ACTIVATE on write channel device %s: " - "function level mismatch " - "(sent: 0x%x, received: 0x%x)\n", - CARD_WDEV_ID(card), card->info.func_level, temp); + "function level mismatch " + "(sent: 0x%x, received: 0x%x)\n", + CARD_WDEV_ID(card), card->info.func_level, temp); goto out; } channel->state = CH_STATE_UP; @@ -1596,8 +1601,13 @@ qeth_idx_read_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) goto out; } if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) { - PRINT_ERR("IDX_ACTIVATE on read channel device %s: negative " - "reply\n", CARD_RDEV_ID(card)); + if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19) + PRINT_ERR("IDX_ACTIVATE on read channel device %s: " + "adapter exclusively used by another host\n", + CARD_RDEV_ID(card)); + else + PRINT_ERR("IDX_ACTIVATE on read channel device %s: " + "negative reply\n", CARD_RDEV_ID(card)); goto out; } @@ -1612,8 +1622,8 @@ qeth_idx_read_cb(struct qeth_channel *channel, struct qeth_cmd_buffer *iob) memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2); if (temp != qeth_peer_func_level(card->info.func_level)) { PRINT_WARN("IDX_ACTIVATE on read channel device %s: function " - "level mismatch (sent: 0x%x, received: 0x%x)\n", - CARD_RDEV_ID(card), card->info.func_level, temp); + "level mismatch (sent: 0x%x, received: 0x%x)\n", + CARD_RDEV_ID(card), card->info.func_level, temp); goto out; } memcpy(&card->token.issuer_rm_r, @@ -1934,6 +1944,7 @@ qeth_send_control_data(struct qeth_card *card, int len, atomic_inc(&reply->received); wake_up(&reply->wait_q); } + cpu_relax(); }; rc = reply->rc; qeth_put_reply(reply); @@ -2258,6 +2269,89 @@ qeth_get_skb(unsigned int length, struct qeth_hdr *hdr) return skb; } +static inline int +qeth_create_skb_frag(struct qdio_buffer_element *element, + struct sk_buff **pskb, + int offset, int *pfrag, int data_len) +{ + struct page *page = virt_to_page(element->addr); + if (*pfrag == 0) { + /* the upper protocol layers assume that there is data in the + * skb itself. Copy a small amount (64 bytes) to make them + * happy. */ + *pskb = dev_alloc_skb(64 + QETH_FAKE_LL_LEN_ETH); + if (!(*pskb)) + return -ENOMEM; + skb_reserve(*pskb, QETH_FAKE_LL_LEN_ETH); + if (data_len <= 64) { + memcpy(skb_put(*pskb, data_len), element->addr + offset, + data_len); + } else { + get_page(page); + memcpy(skb_put(*pskb, 64), element->addr + offset, 64); + skb_fill_page_desc(*pskb, *pfrag, page, offset + 64, + data_len - 64); + (*pskb)->data_len += data_len - 64; + (*pskb)->len += data_len - 64; + (*pskb)->truesize += data_len - 64; + } + } else { + get_page(page); + skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len); + (*pskb)->data_len += data_len; + (*pskb)->len += data_len; + (*pskb)->truesize += data_len; + } + (*pfrag)++; + return 0; +} + +static inline struct qeth_buffer_pool_entry * +qeth_find_free_buffer_pool_entry(struct qeth_card *card) +{ + struct list_head *plh; + struct qeth_buffer_pool_entry *entry; + int i, free; + struct page *page; + + if (list_empty(&card->qdio.in_buf_pool.entry_list)) + return NULL; + + list_for_each(plh, &card->qdio.in_buf_pool.entry_list) { + entry = list_entry(plh, struct qeth_buffer_pool_entry, list); + free = 1; + for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { + if (page_count(virt_to_page(entry->elements[i])) > 1) { + free = 0; + break; + } + } + if (free) { + list_del_init(&entry->list); + return entry; + } + } + + /* no free buffer in pool so take first one and swap pages */ + entry = list_entry(card->qdio.in_buf_pool.entry_list.next, + struct qeth_buffer_pool_entry, list); + for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) { + if (page_count(virt_to_page(entry->elements[i])) > 1) { + page = alloc_page(GFP_ATOMIC|GFP_DMA); + if (!page) { + return NULL; + } else { + free_page((unsigned long)entry->elements[i]); + entry->elements[i] = page_address(page); + if (card->options.performance_stats) + card->perf_stats.sg_alloc_page_rx++; + } + } + } + list_del_init(&entry->list); + return entry; +} + static struct sk_buff * qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, struct qdio_buffer_element **__element, int *__offset, @@ -2269,6 +2363,8 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, int skb_len; void *data_ptr; int data_len; + int use_rx_sg = 0; + int frag = 0; QETH_DBF_TEXT(trace,6,"nextskb"); /* qeth_hdr must not cross element boundaries */ @@ -2293,23 +2389,43 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, if (!skb_len) return NULL; - if (card->options.fake_ll){ - if(card->dev->type == ARPHRD_IEEE802_TR){ - if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR, *hdr))) - goto no_mem; - skb_reserve(skb,QETH_FAKE_LL_LEN_TR); + if ((skb_len >= card->options.rx_sg_cb) && + (!(card->info.type == QETH_CARD_TYPE_OSN)) && + (!atomic_read(&card->force_alloc_skb))) { + use_rx_sg = 1; + } else { + if (card->options.fake_ll) { + if (card->dev->type == ARPHRD_IEEE802_TR) { + if (!(skb = qeth_get_skb(skb_len + + QETH_FAKE_LL_LEN_TR, *hdr))) + goto no_mem; + skb_reserve(skb, QETH_FAKE_LL_LEN_TR); + } else { + if (!(skb = qeth_get_skb(skb_len + + QETH_FAKE_LL_LEN_ETH, *hdr))) + goto no_mem; + skb_reserve(skb, QETH_FAKE_LL_LEN_ETH); + } } else { - if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH, *hdr))) + skb = qeth_get_skb(skb_len, *hdr); + if (!skb) goto no_mem; - skb_reserve(skb,QETH_FAKE_LL_LEN_ETH); } - } else if (!(skb = qeth_get_skb(skb_len, *hdr))) - goto no_mem; + } + data_ptr = element->addr + offset; while (skb_len) { data_len = min(skb_len, (int)(element->length - offset)); - if (data_len) - memcpy(skb_put(skb, data_len), data_ptr, data_len); + if (data_len) { + if (use_rx_sg) { + if (qeth_create_skb_frag(element, &skb, offset, + &frag, data_len)) + goto no_mem; + } else { + memcpy(skb_put(skb, data_len), data_ptr, + data_len); + } + } skb_len -= data_len; if (skb_len){ if (qeth_is_last_sbale(element)){ @@ -2331,6 +2447,10 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer, } *__element = element; *__offset = offset; + if (use_rx_sg && card->options.performance_stats) { + card->perf_stats.sg_skbs_rx++; + card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags; + } return skb; no_mem: if (net_ratelimit()){ @@ -2385,7 +2505,7 @@ qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb, struct iphdr *ip_hdr; QETH_DBF_TEXT(trace,5,"skbfktr"); - skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_TR); + skb_set_mac_header(skb, (int)-QETH_FAKE_LL_LEN_TR); /* this is a fake ethernet header */ fake_hdr = tr_hdr(skb); @@ -2608,28 +2728,15 @@ qeth_process_inbound_buffer(struct qeth_card *card, } } -static struct qeth_buffer_pool_entry * -qeth_get_buffer_pool_entry(struct qeth_card *card) -{ - struct qeth_buffer_pool_entry *entry; - - QETH_DBF_TEXT(trace, 6, "gtbfplen"); - if (!list_empty(&card->qdio.in_buf_pool.entry_list)) { - entry = list_entry(card->qdio.in_buf_pool.entry_list.next, - struct qeth_buffer_pool_entry, list); - list_del_init(&entry->list); - return entry; - } - return NULL; -} - -static void +static int qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) { struct qeth_buffer_pool_entry *pool_entry; int i; - - pool_entry = qeth_get_buffer_pool_entry(card); + + pool_entry = qeth_find_free_buffer_pool_entry(card); + if (!pool_entry) + return 1; /* * since the buffer is accessed only from the input_tasklet * there shouldn't be a need to synchronize; also, since we use @@ -2648,6 +2755,7 @@ qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf) buf->buffer->element[i].flags = 0; } buf->state = QETH_QDIO_BUF_EMPTY; + return 0; } static void @@ -2682,6 +2790,7 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) int count; int i; int rc; + int newcount = 0; QETH_DBF_TEXT(trace,6,"queinbuf"); count = (index < queue->next_buf_to_init)? @@ -2692,9 +2801,30 @@ qeth_queue_input_buffer(struct qeth_card *card, int index) /* only requeue at a certain threshold to avoid SIGAs */ if (count >= QETH_IN_BUF_REQUEUE_THRESHOLD(card)){ for (i = queue->next_buf_to_init; - i < queue->next_buf_to_init + count; ++i) - qeth_init_input_buffer(card, - &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q]); + i < queue->next_buf_to_init + count; ++i) { + if (qeth_init_input_buffer(card, + &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q])) { + break; + } else { + newcount++; + } + } + + if (newcount < count) { + /* we are in memory shortage so we switch back to + traditional skb allocation and drop packages */ + if (!atomic_read(&card->force_alloc_skb) && + net_ratelimit()) + PRINT_WARN("Switch to alloc skb\n"); + atomic_set(&card->force_alloc_skb, 3); + count = newcount; + } else { + if ((atomic_read(&card->force_alloc_skb) == 1) && + net_ratelimit()) + PRINT_WARN("Switch to sg\n"); + atomic_add_unless(&card->force_alloc_skb, -1, 0); + } + /* * according to old code it should be avoided to requeue all * 128 buffers in order to benefit from PCI avoidance. @@ -3236,10 +3366,12 @@ out_freeoutq: while (i > 0) kfree(card->qdio.out_qs[--i]); kfree(card->qdio.out_qs); + card->qdio.out_qs = NULL; out_freepool: qeth_free_buffer_pool(card); out_freeinq: kfree(card->qdio.in_q); + card->qdio.in_q = NULL; out_nomem: atomic_set(&card->qdio.state, QETH_QDIO_UNINITIALIZED); return -ENOMEM; @@ -3255,16 +3387,20 @@ qeth_free_qdio_buffers(struct qeth_card *card) QETH_QDIO_UNINITIALIZED) return; kfree(card->qdio.in_q); + card->qdio.in_q = NULL; /* inbound buffer pool */ qeth_free_buffer_pool(card); /* free outbound qdio_qs */ - for (i = 0; i < card->qdio.no_out_queues; ++i){ - for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) - qeth_clear_output_buffer(card->qdio.out_qs[i], - &card->qdio.out_qs[i]->bufs[j]); - kfree(card->qdio.out_qs[i]); + if (card->qdio.out_qs) { + for (i = 0; i < card->qdio.no_out_queues; ++i) { + for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) + qeth_clear_output_buffer(card->qdio.out_qs[i], + &card->qdio.out_qs[i]->bufs[j]); + kfree(card->qdio.out_qs[i]); + } + kfree(card->qdio.out_qs); + card->qdio.out_qs = NULL; } - kfree(card->qdio.out_qs); } static void @@ -3275,7 +3411,7 @@ qeth_clear_qdio_buffers(struct qeth_card *card) QETH_DBF_TEXT(trace, 2, "clearqdbf"); /* clear outbound buffers to free skbs */ for (i = 0; i < card->qdio.no_out_queues; ++i) - if (card->qdio.out_qs[i]){ + if (card->qdio.out_qs && card->qdio.out_qs[i]) { for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) qeth_clear_output_buffer(card->qdio.out_qs[i], &card->qdio.out_qs[i]->bufs[j]); @@ -4435,6 +4571,53 @@ qeth_get_elements_no(struct qeth_card *card, void *hdr, return elements_needed; } +static void qeth_tx_csum(struct sk_buff *skb) +{ + int tlen; + + if (skb->protocol == htons(ETH_P_IP)) { + tlen = ntohs(ip_hdr(skb)->tot_len) - (ip_hdr(skb)->ihl << 2); + switch (ip_hdr(skb)->protocol) { + case IPPROTO_TCP: + tcp_hdr(skb)->check = 0; + tcp_hdr(skb)->check = csum_tcpudp_magic( + ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, + tlen, ip_hdr(skb)->protocol, + skb_checksum(skb, skb_transport_offset(skb), + tlen, 0)); + break; + case IPPROTO_UDP: + udp_hdr(skb)->check = 0; + udp_hdr(skb)->check = csum_tcpudp_magic( + ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, + tlen, ip_hdr(skb)->protocol, + skb_checksum(skb, skb_transport_offset(skb), + tlen, 0)); + break; + } + } else if (skb->protocol == htons(ETH_P_IPV6)) { + switch (ipv6_hdr(skb)->nexthdr) { + case IPPROTO_TCP: + tcp_hdr(skb)->check = 0; + tcp_hdr(skb)->check = csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, + ipv6_hdr(skb)->payload_len, + ipv6_hdr(skb)->nexthdr, + skb_checksum(skb, skb_transport_offset(skb), + ipv6_hdr(skb)->payload_len, 0)); + break; + case IPPROTO_UDP: + udp_hdr(skb)->check = 0; + udp_hdr(skb)->check = csum_ipv6_magic( + &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, + ipv6_hdr(skb)->payload_len, + ipv6_hdr(skb)->nexthdr, + skb_checksum(skb, skb_transport_offset(skb), + ipv6_hdr(skb)->payload_len, 0)); + break; + } + } +} static int qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) @@ -4520,12 +4703,22 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) elements_needed += elems; } + if ((large_send == QETH_LARGE_SEND_NO) && + (skb->ip_summed == CHECKSUM_PARTIAL)) + qeth_tx_csum(new_skb); + if (card->info.type != QETH_CARD_TYPE_IQD) rc = qeth_do_send_packet(card, queue, new_skb, hdr, elements_needed, ctx); - else + else { + if ((skb->protocol == htons(ETH_P_ARP)) && + (card->dev->flags & IFF_NOARP)) { + __qeth_free_new_skb(skb, new_skb); + return -EPERM; + } rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, elements_needed, ctx); + } if (!rc) { card->stats.tx_packets++; card->stats.tx_bytes += tx_bytes; @@ -6267,20 +6460,18 @@ qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr) static u32 qeth_ethtool_get_tx_csum(struct net_device *dev) { - /* We may need to say that we support tx csum offload if - * we do EDDP or TSO. There are discussions going on to - * enforce rules in the stack and in ethtool that make - * SG and TSO depend on HW_CSUM. At the moment there are - * no such rules.... - * If we say yes here, we have to checksum outbound packets - * any time. */ - return 0; + return (dev->features & NETIF_F_HW_CSUM) != 0; } static int qeth_ethtool_set_tx_csum(struct net_device *dev, u32 data) { - return -EINVAL; + if (data) + dev->features |= NETIF_F_HW_CSUM; + else + dev->features &= ~NETIF_F_HW_CSUM; + + return 0; } static u32 @@ -6494,6 +6685,7 @@ qeth_hardsetup_card(struct qeth_card *card) QETH_DBF_TEXT(setup, 2, "hrdsetup"); + atomic_set(&card->force_alloc_skb, 0); retry: if (retries < 3){ PRINT_WARN("Retrying to do IDX activates.\n"); @@ -7293,7 +7485,8 @@ qeth_start_ipa_tso(struct qeth_card *card) } if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)){ card->options.large_send = QETH_LARGE_SEND_NO; - card->dev->features &= ~ (NETIF_F_TSO | NETIF_F_SG); + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); } return rc; } @@ -7433,22 +7626,26 @@ qeth_set_large_send(struct qeth_card *card, enum qeth_large_send_types type) card->options.large_send = type; switch (card->options.large_send) { case QETH_LARGE_SEND_EDDP: - card->dev->features |= NETIF_F_TSO | NETIF_F_SG; + card->dev->features |= NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM; break; case QETH_LARGE_SEND_TSO: if (qeth_is_supported(card, IPA_OUTBOUND_TSO)){ - card->dev->features |= NETIF_F_TSO | NETIF_F_SG; + card->dev->features |= NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM; } else { PRINT_WARN("TSO not supported on %s. " "large_send set to 'no'.\n", card->dev->name); - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG); + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); card->options.large_send = QETH_LARGE_SEND_NO; rc = -EOPNOTSUPP; } break; default: /* includes QETH_LARGE_SEND_NO */ - card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG); + card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG | + NETIF_F_HW_CSUM); break; } if (card->state == CARD_STATE_UP) diff --git a/drivers/s390/net/qeth_mpc.h b/drivers/s390/net/qeth_mpc.h index 1d8083c91765..6de2da5ed5fd 100644 --- a/drivers/s390/net/qeth_mpc.h +++ b/drivers/s390/net/qeth_mpc.h @@ -565,6 +565,7 @@ extern unsigned char IDX_ACTIVATE_WRITE[]; #define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20) #define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2) #define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12) +#define QETH_IDX_ACT_CAUSE_CODE(buffer) (buffer)[0x09] #define PDU_ENCAPSULATION(buffer) \ (buffer + *(buffer + (*(buffer+0x0b)) + \ diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index 89d56c8ecdd2..f1ff165a5e05 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c @@ -212,6 +212,12 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it) " Skb fragments sent in SG mode : %u\n\n", card->perf_stats.sg_skbs_sent, card->perf_stats.sg_frags_sent); + seq_printf(s, " Skbs received in SG mode : %u\n" + " Skb fragments received in SG mode : %u\n" + " Page allocations for rx SG mode : %u\n\n", + card->perf_stats.sg_skbs_rx, + card->perf_stats.sg_frags_rx, + card->perf_stats.sg_alloc_page_rx); seq_printf(s, " large_send tx (in Kbytes) : %u\n" " large_send count : %u\n\n", card->perf_stats.large_send_bytes >> 10, diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c index 65ffc21afc37..2cc3f3a0e393 100644 --- a/drivers/s390/net/qeth_sys.c +++ b/drivers/s390/net/qeth_sys.c @@ -991,7 +991,7 @@ static struct attribute_group qeth_osn_device_attr_group = { #define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \ struct device_attribute dev_attr_##_id = { \ - .attr = {.name=__stringify(_name), .mode=_mode, .owner=THIS_MODULE },\ + .attr = {.name=__stringify(_name), .mode=_mode, },\ .show = _show, \ .store = _store, \ }; @@ -1760,10 +1760,10 @@ qeth_remove_device_attributes(struct device *dev) { struct qeth_card *card = dev->driver_data; - if (card->info.type == QETH_CARD_TYPE_OSN) - return sysfs_remove_group(&dev->kobj, - &qeth_osn_device_attr_group); - + if (card->info.type == QETH_CARD_TYPE_OSN) { + sysfs_remove_group(&dev->kobj, &qeth_osn_device_attr_group); + return; + } sysfs_remove_group(&dev->kobj, &qeth_device_attr_group); sysfs_remove_group(&dev->kobj, &qeth_device_ipato_group); sysfs_remove_group(&dev->kobj, &qeth_device_vipa_group); diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 821cde65e369..90aa53fc4f3e 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -259,21 +259,21 @@ zfcp_module_init(void) size = sizeof(struct zfcp_fsf_req_qtcb); align = calc_alignment(size); zfcp_data.fsf_req_qtcb_cache = - kmem_cache_create("zfcp_fsf", size, align, 0, NULL, NULL); + kmem_cache_create("zfcp_fsf", size, align, 0, NULL); if (!zfcp_data.fsf_req_qtcb_cache) goto out; size = sizeof(struct fsf_status_read_buffer); align = calc_alignment(size); zfcp_data.sr_buffer_cache = - kmem_cache_create("zfcp_sr", size, align, 0, NULL, NULL); + kmem_cache_create("zfcp_sr", size, align, 0, NULL); if (!zfcp_data.sr_buffer_cache) goto out_sr_cache; size = sizeof(struct zfcp_gid_pn_data); align = calc_alignment(size); zfcp_data.gid_pn_cache = - kmem_cache_create("zfcp_gid", size, align, 0, NULL, NULL); + kmem_cache_create("zfcp_gid", size, align, 0, NULL); if (!zfcp_data.gid_pn_cache) goto out_gid_cache; @@ -815,9 +815,7 @@ zfcp_get_adapter_by_busid(char *bus_id) struct zfcp_unit * zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) { - struct zfcp_unit *unit, *tmp_unit; - unsigned int scsi_lun; - int found; + struct zfcp_unit *unit; /* * check that there is no unit with this FCP_LUN already in list @@ -863,22 +861,10 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) } zfcp_unit_get(unit); + unit->scsi_lun = scsilun_to_int((struct scsi_lun *)&unit->fcp_lun); - scsi_lun = 0; - found = 0; write_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(tmp_unit, &port->unit_list_head, list) { - if (tmp_unit->scsi_lun != scsi_lun) { - found = 1; - break; - } - scsi_lun++; - } - unit->scsi_lun = scsi_lun; - if (found) - list_add_tail(&unit->list, &tmp_unit->list); - else - list_add_tail(&unit->list, &port->unit_list_head); + list_add_tail(&unit->list, &port->unit_list_head); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); write_unlock_irq(&zfcp_data.config_lock); @@ -1517,7 +1503,7 @@ zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool) data->ct.pool = pool; } } else { - data = kmalloc(sizeof(struct zfcp_gid_pn_data), GFP_ATOMIC); + data = kmem_cache_alloc(zfcp_data.gid_pn_cache, GFP_ATOMIC); } if (NULL == data) @@ -1540,15 +1526,12 @@ zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool) * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed */ -static void -zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) +static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn) { - if ((gid_pn->ct.pool != 0)) + if (gid_pn->ct.pool) mempool_free(gid_pn, gid_pn->ct.pool); else - kfree(gid_pn); - - return; + kmem_cache_free(zfcp_data.gid_pn_cache, gid_pn); } /** diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 22649639230b..b36dfc40d9fa 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -126,6 +126,7 @@ zfcp_address_to_sg(void *address, struct scatterlist *list) #define ZFCP_MIN_OUTPUT_THRESHOLD 1 /* ignored by QDIO layer */ #define QDIO_SCSI_QFMT 1 /* 1 for FSF */ +#define QBUFF_PER_PAGE (PAGE_SIZE / sizeof(struct qdio_buffer)) /********************* FSF SPECIFIC DEFINES *********************************/ diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index aef66bc2b6ca..d8cd75ce2d9a 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1626,7 +1626,7 @@ zfcp_erp_schedule_work(struct zfcp_unit *unit) { struct zfcp_erp_add_work *p; - p = kmalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) { ZFCP_LOG_NORMAL("error: Out of resources. Could not register " "the FCP-LUN 0x%Lx connected to " @@ -1639,7 +1639,6 @@ zfcp_erp_schedule_work(struct zfcp_unit *unit) } zfcp_unit_get(unit); - memset(p, 0, sizeof(*p)); atomic_set_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING, &unit->status); INIT_WORK(&p->work, zfcp_erp_scsi_scan); p->unit = unit; @@ -1986,6 +1985,10 @@ zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close) failed_openfcp: zfcp_close_fsf(erp_action->adapter); failed_qdio: + atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK | + ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED | + ZFCP_STATUS_ADAPTER_XPORT_OK, + &erp_action->adapter->status); out: return retval; } @@ -2167,6 +2170,9 @@ zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action) sleep *= 2; } + atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, + &adapter->status); + if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status)) { ZFCP_LOG_INFO("error: exchange of configuration data for " diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 0eb31e162b15..99299976e891 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1930,7 +1930,7 @@ static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req) skip_fsfstatus: send_els->status = retval; - if (send_els->handler != 0) + if (send_els->handler) send_els->handler(send_els->handler_data); return retval; @@ -4154,8 +4154,9 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req) fcp_rsp_iu->fcp_resid, (int) zfcp_get_fcp_dl(fcp_cmnd_iu)); - scpnt->resid = fcp_rsp_iu->fcp_resid; - if (scpnt->request_bufflen - scpnt->resid < scpnt->underflow) + scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid); + if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) < + scpnt->underflow) set_host_byte(&scpnt->result, DID_ERROR); } diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index bdf5782b8a7a..c6899efdc8f6 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -36,8 +36,6 @@ static void zfcp_qdio_sbale_fill (struct zfcp_fsf_req *, unsigned long, void *, int); static int zfcp_qdio_sbals_from_segment (struct zfcp_fsf_req *, unsigned long, void *, unsigned long); -static int zfcp_qdio_sbals_from_buffer - (struct zfcp_fsf_req *, unsigned long, void *, unsigned long, int); static qdio_handler_t zfcp_qdio_request_handler; static qdio_handler_t zfcp_qdio_response_handler; @@ -47,103 +45,56 @@ static int zfcp_qdio_handler_error_check(struct zfcp_adapter *, #define ZFCP_LOG_AREA ZFCP_LOG_AREA_QDIO /* - * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t - * array in the adapter struct. - * Cur_buf is the pointer array and count can be any number of required - * buffers, the page-fitting arithmetic is done entirely within this funciton. + * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array + * in the adapter struct sbuf is the pointer array. * - * returns: number of buffers allocated * locks: must only be called with zfcp_data.config_sema taken */ -static int -zfcp_qdio_buffers_enqueue(struct qdio_buffer **cur_buf, int count) +static void +zfcp_qdio_buffers_dequeue(struct qdio_buffer **sbuf) { - int buf_pos; - int qdio_buffers_per_page; - int page_pos = 0; - struct qdio_buffer *first_in_page = NULL; - - qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer); - ZFCP_LOG_TRACE("buffers_per_page=%d\n", qdio_buffers_per_page); - - for (buf_pos = 0; buf_pos < count; buf_pos++) { - if (page_pos == 0) { - cur_buf[buf_pos] = (struct qdio_buffer *) - get_zeroed_page(GFP_KERNEL); - if (cur_buf[buf_pos] == NULL) { - ZFCP_LOG_INFO("error: allocation of " - "QDIO buffer failed \n"); - goto out; - } - first_in_page = cur_buf[buf_pos]; - } else { - cur_buf[buf_pos] = first_in_page + page_pos; + int pos; - } - /* was initialised to zero */ - page_pos++; - page_pos %= qdio_buffers_per_page; - } - out: - return buf_pos; + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) + free_page((unsigned long) sbuf[pos]); } /* - * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array - * in the adapter struct cur_buf is the pointer array and count can be any - * number of buffers in the array that should be freed starting from buffer 0 + * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t + * array in the adapter struct. + * Cur_buf is the pointer array * + * returns: zero on success else -ENOMEM * locks: must only be called with zfcp_data.config_sema taken */ -static void -zfcp_qdio_buffers_dequeue(struct qdio_buffer **cur_buf, int count) +static int +zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbuf) { - int buf_pos; - int qdio_buffers_per_page; - - qdio_buffers_per_page = PAGE_SIZE / sizeof (struct qdio_buffer); - ZFCP_LOG_TRACE("buffers_per_page=%d\n", qdio_buffers_per_page); + int pos; - for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page) - free_page((unsigned long) cur_buf[buf_pos]); - return; + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) { + sbuf[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL); + if (!sbuf[pos]) { + zfcp_qdio_buffers_dequeue(sbuf); + return -ENOMEM; + } + } + for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++) + if (pos % QBUFF_PER_PAGE) + sbuf[pos] = sbuf[pos - 1] + 1; + return 0; } /* locks: must only be called with zfcp_data.config_sema taken */ int zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter) { - int buffer_count; - int retval = 0; - - buffer_count = - zfcp_qdio_buffers_enqueue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) { - ZFCP_LOG_DEBUG("only %d QDIO buffers allocated for request " - "queue\n", buffer_count); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - buffer_count); - retval = -ENOMEM; - goto out; - } + int ret; - buffer_count = - zfcp_qdio_buffers_enqueue(&(adapter->response_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) { - ZFCP_LOG_DEBUG("only %d QDIO buffers allocated for response " - "queue", buffer_count); - zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]), - buffer_count); - ZFCP_LOG_TRACE("freeing request_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); - retval = -ENOMEM; - goto out; - } - out: - return retval; + ret = zfcp_qdio_buffers_enqueue(adapter->request_queue.buffer); + if (ret) + return ret; + return zfcp_qdio_buffers_enqueue(adapter->response_queue.buffer); } /* locks: must only be called with zfcp_data.config_sema taken */ @@ -151,12 +102,10 @@ void zfcp_qdio_free_queues(struct zfcp_adapter *adapter) { ZFCP_LOG_TRACE("freeing request_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->request_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); + zfcp_qdio_buffers_dequeue(adapter->request_queue.buffer); ZFCP_LOG_TRACE("freeing response_queue buffers\n"); - zfcp_qdio_buffers_dequeue(&(adapter->response_queue.buffer[0]), - QDIO_MAX_BUFFERS_PER_Q); + zfcp_qdio_buffers_dequeue(adapter->response_queue.buffer); } int @@ -681,28 +630,6 @@ out: /** - * zfcp_qdio_sbals_from_buffer - fill SBALs from buffer - * @fsf_req: request to be processed - * @sbtype: SBALE flags - * @buffer: data buffer - * @length: length of buffer - * @max_sbals: upper bound for number of SBALs to be used - */ -static int -zfcp_qdio_sbals_from_buffer(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, - void *buffer, unsigned long length, int max_sbals) -{ - struct scatterlist sg_segment; - - zfcp_address_to_sg(buffer, &sg_segment); - sg_segment.length = length; - - return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, &sg_segment, 1, - max_sbals); -} - - -/** * zfcp_qdio_sbals_from_scsicmnd - fill SBALs from scsi command * @fsf_req: request to be processed * @sbtype: SBALE flags @@ -713,18 +640,9 @@ int zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req, unsigned long sbtype, struct scsi_cmnd *scsi_cmnd) { - if (scsi_cmnd->use_sg) { - return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, - (struct scatterlist *) - scsi_cmnd->request_buffer, - scsi_cmnd->use_sg, - ZFCP_MAX_SBALS_PER_REQ); - } else { - return zfcp_qdio_sbals_from_buffer(fsf_req, sbtype, - scsi_cmnd->request_buffer, - scsi_cmnd->request_bufflen, - ZFCP_MAX_SBALS_PER_REQ); - } + return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, scsi_sglist(scsi_cmnd), + scsi_sg_count(scsi_cmnd), + ZFCP_MAX_SBALS_PER_REQ); } /** diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0acf6db0a08d..ad7eb4a9261c 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -764,7 +764,9 @@ zfcp_reset_fc_host_stats(struct Scsi_Host *shost) return; ret = zfcp_fsf_exchange_port_data(NULL, adapter, data); - if (ret == 0) { + if (ret) { + kfree(data); + } else { adapter->stats_reset = jiffies/HZ; old_data = adapter->stats_reset_data; adapter->stats_reset_data = data; /* finally freed in |