diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/block/dasd_alias.c | 4 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 16 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 147 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.h | 184 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 1 | ||||
-rw-r--r-- | drivers/s390/char/sclp.c | 6 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 5 | ||||
-rw-r--r-- | drivers/s390/char/sclp_config.c | 13 | ||||
-rw-r--r-- | drivers/s390/cio/idset.c | 8 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_main.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_perf.c | 2 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_setup.c | 4 | ||||
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core.h | 5 | ||||
-rw-r--r-- | drivers/s390/net/qeth_core_main.c | 63 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l2_main.c | 50 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 51 |
17 files changed, 336 insertions, 227 deletions
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 2d8df0b30538..20676cdef4a5 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -91,7 +91,8 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu, else search_unit_addr = uid->base_unit_addr; list_for_each_entry(pos, &lcu->grouplist, group) { - if (pos->uid.base_unit_addr == search_unit_addr) + if (pos->uid.base_unit_addr == search_unit_addr && + !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit))) return pos; }; return NULL; @@ -332,6 +333,7 @@ static int _add_device_to_lcu(struct alias_lcu *lcu, group->uid.base_unit_addr = uid->real_unit_addr; else group->uid.base_unit_addr = uid->base_unit_addr; + memcpy(group->uid.vduit, uid->vduit, sizeof(uid->vduit)); INIT_LIST_HEAD(&group->group); INIT_LIST_HEAD(&group->baselist); INIT_LIST_HEAD(&group->aliaslist); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index d774e79476fe..cd3335c1c307 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -913,7 +913,8 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL); #define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\ - /* SSID */ 4 + 1 + /* unit addr */ 2 + 1) + /* SSID */ 4 + 1 + /* unit addr */ 2 + 1 +\ + /* vduit */ 32 + 1) static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -945,8 +946,17 @@ dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) sprintf(ua_string, "%02x", uid->real_unit_addr); break; } - snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s", - uid->vendor, uid->serial, uid->ssid, ua_string); + if (strlen(uid->vduit) > 0) + snprintf(uid_string, sizeof(uid_string), + "%s.%s.%04x.%s.%s", + uid->vendor, uid->serial, + uid->ssid, ua_string, + uid->vduit); + else + snprintf(uid_string, sizeof(uid_string), + "%s.%s.%04x.%s", + uid->vendor, uid->serial, + uid->ssid, ua_string); spin_unlock(&dasd_devmap_lock); return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); } diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 3590fdb5b2fd..773b3fe275b2 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -313,8 +313,8 @@ static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk, memset(pfxdata, 0, sizeof(*pfxdata)); /* prefix data */ pfxdata->format = 0; - pfxdata->base_address = basepriv->conf_data.ned1.unit_addr; - pfxdata->base_lss = basepriv->conf_data.ned1.ID; + pfxdata->base_address = basepriv->ned->unit_addr; + pfxdata->base_lss = basepriv->ned->ID; pfxdata->validity.define_extend = 1; /* private uid is kept up to date, conf_data may be outdated */ @@ -536,36 +536,40 @@ dasd_eckd_cdl_reclen(int recid) /* * Generate device unique id that specifies the physical device. */ -static int -dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) +static int dasd_eckd_generate_uid(struct dasd_device *device, + struct dasd_uid *uid) { struct dasd_eckd_private *private; - struct dasd_eckd_confdata *confdata; + int count; private = (struct dasd_eckd_private *) device->private; if (!private) return -ENODEV; - confdata = &private->conf_data; - if (!confdata) + if (!private->ned || !private->gneq) return -ENODEV; memset(uid, 0, sizeof(struct dasd_uid)); - memcpy(uid->vendor, confdata->ned1.HDA_manufacturer, + memcpy(uid->vendor, private->ned->HDA_manufacturer, sizeof(uid->vendor) - 1); EBCASC(uid->vendor, sizeof(uid->vendor) - 1); - memcpy(uid->serial, confdata->ned1.HDA_location, + memcpy(uid->serial, private->ned->HDA_location, sizeof(uid->serial) - 1); EBCASC(uid->serial, sizeof(uid->serial) - 1); - uid->ssid = confdata->neq.subsystemID; - uid->real_unit_addr = confdata->ned1.unit_addr; - if (confdata->ned2.sneq.flags == 0x40 && - confdata->ned2.sneq.format == 0x0001) { - uid->type = confdata->ned2.sneq.sua_flags; + uid->ssid = private->gneq->subsystemID; + uid->real_unit_addr = private->ned->unit_addr;; + if (private->sneq) { + uid->type = private->sneq->sua_flags; if (uid->type == UA_BASE_PAV_ALIAS) - uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr; + uid->base_unit_addr = private->sneq->base_unit_addr; } else { uid->type = UA_BASE_DEVICE; } + if (private->vdsneq) { + for (count = 0; count < 16; count++) { + sprintf(uid->vduit+2*count, "%02x", + private->vdsneq->uit[count]); + } + } return 0; } @@ -623,6 +627,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, ret = -ENOMEM; goto out_error; } + + /* + * buffer has to start with EBCDIC "V1.0" to show + * support for virtual device SNEQ + */ + rcd_buf[0] = 0xE5; + rcd_buf[1] = 0xF1; + rcd_buf[2] = 0x4B; + rcd_buf[3] = 0xF0; cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); if (IS_ERR(cqr)) { ret = PTR_ERR(cqr); @@ -646,8 +659,62 @@ out_error: return ret; } -static int -dasd_eckd_read_conf(struct dasd_device *device) +static int dasd_eckd_identify_conf_parts(struct dasd_eckd_private *private) +{ + + struct dasd_sneq *sneq; + int i, count; + + private->ned = NULL; + private->sneq = NULL; + private->vdsneq = NULL; + private->gneq = NULL; + count = private->conf_len / sizeof(struct dasd_sneq); + sneq = (struct dasd_sneq *)private->conf_data; + for (i = 0; i < count; ++i) { + if (sneq->flags.identifier == 1 && sneq->format == 1) + private->sneq = sneq; + else if (sneq->flags.identifier == 1 && sneq->format == 4) + private->vdsneq = (struct vd_sneq *)sneq; + else if (sneq->flags.identifier == 2) + private->gneq = (struct dasd_gneq *)sneq; + else if (sneq->flags.identifier == 3 && sneq->res1 == 1) + private->ned = (struct dasd_ned *)sneq; + sneq++; + } + if (!private->ned || !private->gneq) { + private->ned = NULL; + private->sneq = NULL; + private->vdsneq = NULL; + private->gneq = NULL; + return -EINVAL; + } + return 0; + +}; + +static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len) +{ + struct dasd_gneq *gneq; + int i, count, found; + + count = conf_len / sizeof(*gneq); + gneq = (struct dasd_gneq *)conf_data; + found = 0; + for (i = 0; i < count; ++i) { + if (gneq->flags.identifier == 2) { + found = 1; + break; + } + gneq++; + } + if (found) + return ((char *)gneq)[18] & 0x07; + else + return 0; +} + +static int dasd_eckd_read_conf(struct dasd_device *device) { void *conf_data; int conf_len, conf_data_saved; @@ -661,7 +728,6 @@ dasd_eckd_read_conf(struct dasd_device *device) path_data->opm = ccw_device_get_path_mask(device->cdev); lpm = 0x80; conf_data_saved = 0; - /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { if (lpm & path_data->opm){ @@ -678,22 +744,20 @@ dasd_eckd_read_conf(struct dasd_device *device) "data retrieved"); continue; /* no error */ } - if (conf_len != sizeof(struct dasd_eckd_confdata)) { - MESSAGE(KERN_WARNING, - "sizes of configuration data mismatch" - "%d (read) vs %ld (expected)", - conf_len, - sizeof(struct dasd_eckd_confdata)); - kfree(conf_data); - continue; /* no error */ - } /* save first valid configuration data */ - if (!conf_data_saved){ - memcpy(&private->conf_data, conf_data, - sizeof(struct dasd_eckd_confdata)); + if (!conf_data_saved) { + kfree(private->conf_data); + private->conf_data = conf_data; + private->conf_len = conf_len; + if (dasd_eckd_identify_conf_parts(private)) { + private->conf_data = NULL; + private->conf_len = 0; + kfree(conf_data); + continue; + } conf_data_saved++; } - switch (((char *)conf_data)[242] & 0x07){ + switch (dasd_eckd_path_access(conf_data, conf_len)) { case 0x02: path_data->npm |= lpm; break; @@ -701,7 +765,8 @@ dasd_eckd_read_conf(struct dasd_device *device) path_data->ppm |= lpm; break; } - kfree(conf_data); + if (conf_data != private->conf_data) + kfree(conf_data); } } return 0; @@ -952,6 +1017,7 @@ out_err2: dasd_free_block(device->block); device->block = NULL; out_err1: + kfree(private->conf_data); kfree(device->private); device->private = NULL; return rc; @@ -959,7 +1025,17 @@ out_err1: static void dasd_eckd_uncheck_device(struct dasd_device *device) { + struct dasd_eckd_private *private; + + private = (struct dasd_eckd_private *) device->private; dasd_alias_disconnect_device_from_lcu(device); + private->ned = NULL; + private->sneq = NULL; + private->vdsneq = NULL; + private->gneq = NULL; + private->conf_len = 0; + kfree(private->conf_data); + private->conf_data = NULL; } static struct dasd_ccw_req * @@ -1746,9 +1822,10 @@ dasd_eckd_fill_info(struct dasd_device * device, info->characteristics_size = sizeof(struct dasd_eckd_characteristics); memcpy(info->characteristics, &private->rdc_data, sizeof(struct dasd_eckd_characteristics)); - info->confdata_size = sizeof(struct dasd_eckd_confdata); - memcpy(info->configuration_data, &private->conf_data, - sizeof(struct dasd_eckd_confdata)); + info->confdata_size = min((unsigned long)private->conf_len, + sizeof(info->configuration_data)); + memcpy(info->configuration_data, private->conf_data, + info->confdata_size); return 0; } diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index fc2509c939bc..4bf0aa5112c1 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -231,133 +231,62 @@ struct dasd_eckd_characteristics { __u8 reserved3[10]; } __attribute__ ((packed)); -struct dasd_eckd_confdata { +/* elements of the configuration data */ +struct dasd_ned { struct { - struct { - unsigned char identifier:2; - unsigned char token_id:1; - unsigned char sno_valid:1; - unsigned char subst_sno:1; - unsigned char recNED:1; - unsigned char emuNED:1; - unsigned char reserved:1; - } __attribute__ ((packed)) flags; - __u8 descriptor; - __u8 dev_class; - __u8 reserved; - unsigned char dev_type[6]; - unsigned char dev_model[3]; - unsigned char HDA_manufacturer[3]; - unsigned char HDA_location[2]; - unsigned char HDA_seqno[12]; - __u8 ID; - __u8 unit_addr; - } __attribute__ ((packed)) ned1; - union { - struct { - struct { - unsigned char identifier:2; - unsigned char token_id:1; - unsigned char sno_valid:1; - unsigned char subst_sno:1; - unsigned char recNED:1; - unsigned char emuNED:1; - unsigned char reserved:1; - } __attribute__ ((packed)) flags; - __u8 descriptor; - __u8 reserved[2]; - unsigned char dev_type[6]; - unsigned char dev_model[3]; - unsigned char DASD_manufacturer[3]; - unsigned char DASD_location[2]; - unsigned char DASD_seqno[12]; - __u16 ID; - } __attribute__ ((packed)) ned; - struct { - unsigned char flags; /* byte 0 */ - unsigned char res1; /* byte 1 */ - __u16 format; /* byte 2-3 */ - unsigned char res2[4]; /* byte 4-7 */ - unsigned char sua_flags; /* byte 8 */ - __u8 base_unit_addr; /* byte 9 */ - unsigned char res3[22]; /* byte 10-31 */ - } __attribute__ ((packed)) sneq; - } __attribute__ ((packed)) ned2; + __u8 identifier:2; + __u8 token_id:1; + __u8 sno_valid:1; + __u8 subst_sno:1; + __u8 recNED:1; + __u8 emuNED:1; + __u8 reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 dev_class; + __u8 reserved; + __u8 dev_type[6]; + __u8 dev_model[3]; + __u8 HDA_manufacturer[3]; + __u8 HDA_location[2]; + __u8 HDA_seqno[12]; + __u8 ID; + __u8 unit_addr; +} __attribute__ ((packed)); + +struct dasd_sneq { struct { - struct { - unsigned char identifier:2; - unsigned char token_id:1; - unsigned char sno_valid:1; - unsigned char subst_sno:1; - unsigned char recNED:1; - unsigned char emuNED:1; - unsigned char reserved:1; - } __attribute__ ((packed)) flags; - __u8 descriptor; - __u8 reserved[2]; - unsigned char cont_type[6]; - unsigned char cont_model[3]; - unsigned char cont_manufacturer[3]; - unsigned char cont_location[2]; - unsigned char cont_seqno[12]; - __u16 ID; - } __attribute__ ((packed)) ned3; + __u8 identifier:2; + __u8 reserved:6; + } __attribute__ ((packed)) flags; + __u8 res1; + __u16 format; + __u8 res2[4]; /* byte 4- 7 */ + __u8 sua_flags; /* byte 8 */ + __u8 base_unit_addr; /* byte 9 */ + __u8 res3[22]; /* byte 10-31 */ +} __attribute__ ((packed)); + +struct vd_sneq { struct { - struct { - unsigned char identifier:2; - unsigned char token_id:1; - unsigned char sno_valid:1; - unsigned char subst_sno:1; - unsigned char recNED:1; - unsigned char emuNED:1; - unsigned char reserved:1; - } __attribute__ ((packed)) flags; - __u8 descriptor; - __u8 reserved[2]; - unsigned char cont_type[6]; - unsigned char empty[3]; - unsigned char cont_manufacturer[3]; - unsigned char cont_location[2]; - unsigned char cont_seqno[12]; - __u16 ID; - } __attribute__ ((packed)) ned4; - unsigned char ned5[32]; - unsigned char ned6[32]; - unsigned char ned7[32]; + __u8 identifier:2; + __u8 reserved:6; + } __attribute__ ((packed)) flags; + __u8 res1; + __u16 format; + __u8 res2[4]; /* byte 4- 7 */ + __u8 uit[16]; /* byte 8-23 */ + __u8 res3[8]; /* byte 24-31 */ +} __attribute__ ((packed)); + +struct dasd_gneq { struct { - struct { - unsigned char identifier:2; - unsigned char reserved:6; - } __attribute__ ((packed)) flags; - __u8 selector; - __u16 interfaceID; - __u32 reserved; - __u16 subsystemID; - struct { - unsigned char sp0:1; - unsigned char sp1:1; - unsigned char reserved:5; - unsigned char scluster:1; - } __attribute__ ((packed)) spathID; - __u8 unit_address; - __u8 dev_ID; - __u8 dev_address; - __u8 adapterID; - __u16 link_address; - struct { - unsigned char parallel:1; - unsigned char escon:1; - unsigned char reserved:1; - unsigned char ficon:1; - unsigned char reserved2:4; - } __attribute__ ((packed)) protocol_type; - struct { - unsigned char PID_in_236:1; - unsigned char reserved:7; - } __attribute__ ((packed)) format_flags; - __u8 log_dev_address; - unsigned char reserved2[12]; - } __attribute__ ((packed)) neq; + __u8 identifier:2; + __u8 reserved:6; + } __attribute__ ((packed)) flags; + __u8 reserved[7]; + __u16 subsystemID; + __u8 reserved2[22]; } __attribute__ ((packed)); struct dasd_eckd_path { @@ -463,7 +392,14 @@ struct alias_pav_group { struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; - struct dasd_eckd_confdata conf_data; + u8 *conf_data; + int conf_len; + /* pointers to specific parts in the conf_data */ + struct dasd_ned *ned; + struct dasd_sneq *sneq; + struct vd_sneq *vdsneq; + struct dasd_gneq *gneq; + struct dasd_eckd_path path_data; struct eckd_count count_area[5]; int init_cqr_status; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index fb2f931cf844..31ecaa4a40e4 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -307,6 +307,7 @@ struct dasd_uid { __u16 ssid; __u8 real_unit_addr; __u8 base_unit_addr; + char vduit[33]; }; /* diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 3c8b25e6c345..1fd8f2193ed8 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -399,6 +399,7 @@ sclp_tod_from_jiffies(unsigned long jiffies) void sclp_sync_wait(void) { + unsigned long long old_tick; unsigned long flags; unsigned long cr0, cr0_sync; u64 timeout; @@ -419,11 +420,12 @@ sclp_sync_wait(void) if (!irq_context) local_bh_disable(); /* Enable service-signal interruption, disable timer interrupts */ + old_tick = local_tick_disable(); trace_hardirqs_on(); __ctl_store(cr0, 0, 0); cr0_sync = cr0; + cr0_sync &= 0xffff00a0; cr0_sync |= 0x00000200; - cr0_sync &= 0xFFFFF3AC; __ctl_load(cr0_sync, 0, 0); __raw_local_irq_stosm(0x01); /* Loop until driver state indicates finished request */ @@ -439,9 +441,9 @@ sclp_sync_wait(void) __ctl_load(cr0, 0, 0); if (!irq_context) _local_bh_enable(); + local_tick_enable(old_tick); local_irq_restore(flags); } - EXPORT_SYMBOL(sclp_sync_wait); /* Dispatch changes in send and receive mask to registered listeners. */ diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 0c2b77493db4..eb5f1b8bc57f 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -427,6 +427,8 @@ static int sclp_mem_notifier(struct notifier_block *nb, sclp_attach_storage(id); switch (action) { case MEM_ONLINE: + case MEM_GOING_OFFLINE: + case MEM_CANCEL_OFFLINE: break; case MEM_GOING_ONLINE: rc = sclp_mem_change_state(start, size, 1); @@ -434,6 +436,9 @@ static int sclp_mem_notifier(struct notifier_block *nb, case MEM_CANCEL_ONLINE: sclp_mem_change_state(start, size, 0); break; + case MEM_OFFLINE: + sclp_mem_change_state(start, size, 0); + break; default: rc = -EINVAL; break; diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index fff4ff485d9b..4cebd6ee6d27 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -8,7 +8,6 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/cpu.h> -#include <linux/kthread.h> #include <linux/sysdev.h> #include <linux/workqueue.h> #include <asm/smp.h> @@ -41,19 +40,9 @@ static void sclp_cpu_capability_notify(struct work_struct *work) put_online_cpus(); } -static int sclp_cpu_kthread(void *data) -{ - smp_rescan_cpus(); - return 0; -} - static void __ref sclp_cpu_change_notify(struct work_struct *work) { - /* Can't call smp_rescan_cpus() from workqueue context since it may - * deadlock in case of cpu hotplug. So we have to create a kernel - * thread in order to call it. - */ - kthread_run(sclp_cpu_kthread, NULL, "cpu_rescan"); + smp_rescan_cpus(); } static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c index ef7bc0a125ef..cf8f24a4b5eb 100644 --- a/drivers/s390/cio/idset.c +++ b/drivers/s390/cio/idset.c @@ -5,7 +5,7 @@ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> */ -#include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/bitops.h> #include "idset.h" #include "css.h" @@ -25,18 +25,18 @@ static struct idset *idset_new(int num_ssid, int num_id) { struct idset *set; - set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id), - GFP_KERNEL); + set = vmalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id)); if (set) { set->num_ssid = num_ssid; set->num_id = num_id; + memset(set->bitmap, 0, bitmap_size(num_ssid, num_id)); } return set; } void idset_free(struct idset *set) { - kfree(set); + vfree(set); } void idset_clear(struct idset *set) diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index d10c73cc1688..d15648514a0f 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1355,7 +1355,7 @@ int qdio_allocate(struct qdio_initialize *init_data) goto out_rel; /* qdr is used in ccw1.cda which is u32 */ - irq_ptr->qdr = kzalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); + irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!irq_ptr->qdr) goto out_rel; WARN_ON((unsigned long)irq_ptr->qdr & 0xfff); diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c index ea01b85b1cc9..ec5c4a414235 100644 --- a/drivers/s390/cio/qdio_perf.c +++ b/drivers/s390/cio/qdio_perf.c @@ -142,7 +142,7 @@ int __init qdio_setup_perf_stats(void) return 0; } -void __exit qdio_remove_perf_stats(void) +void qdio_remove_perf_stats(void) { #ifdef CONFIG_PROC_FS remove_proc_entry("qdio_perf", NULL); diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index f0923a8aceda..1bd2a208db28 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -325,7 +325,7 @@ void qdio_release_memory(struct qdio_irq *irq_ptr) kmem_cache_free(qdio_q_cache, q); } } - kfree(irq_ptr->qdr); + free_page((unsigned long) irq_ptr->qdr); free_page(irq_ptr->chsc_page); free_page((unsigned long) irq_ptr); } @@ -515,7 +515,7 @@ int __init qdio_setup_init(void) return 0; } -void __exit qdio_setup_exit(void) +void qdio_setup_exit(void) { kmem_cache_destroy(qdio_q_cache); } diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 79954bd6bfa5..292b60da6dc7 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -352,7 +352,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) return len; } -void s390_virtio_console_init(void) +void __init s390_virtio_console_init(void) { virtio_cons_early_init(early_put_chars); } diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 1895dbb553cd..80971c21ea1a 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -419,6 +419,7 @@ struct qeth_qdio_out_buffer { int next_element_to_fill; struct sk_buff_head skb_list; struct list_head ctx_list; + int is_header[16]; }; struct qeth_card; @@ -785,7 +786,7 @@ void qeth_core_remove_osn_attributes(struct device *); /* exports for qeth discipline device drivers */ extern struct qeth_card_list_struct qeth_core_card_list; - +extern struct kmem_cache *qeth_core_header_cache; extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS]; void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int); @@ -843,7 +844,7 @@ int qeth_get_priority_queue(struct qeth_card *, struct sk_buff *, int, int); int qeth_get_elements_no(struct qeth_card *, void *, struct sk_buff *, int); int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, - struct qeth_eddp_context *); + struct qeth_eddp_context *, int, int); int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, struct qeth_eddp_context *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index cebb25e36e82..bd420d1b9a0d 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -19,8 +19,8 @@ #include <linux/mii.h> #include <linux/kthread.h> -#include <asm-s390/ebcdic.h> -#include <asm-s390/io.h> +#include <asm/ebcdic.h> +#include <asm/io.h> #include <asm/s390_rdev.h> #include "qeth_core.h" @@ -48,6 +48,8 @@ EXPORT_SYMBOL_GPL(qeth_dbf); struct qeth_card_list_struct qeth_core_card_list; EXPORT_SYMBOL_GPL(qeth_core_card_list); +struct kmem_cache *qeth_core_header_cache; +EXPORT_SYMBOL_GPL(qeth_core_header_cache); static struct device *qeth_core_root_dev; static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY; @@ -933,6 +935,10 @@ static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, } qeth_eddp_buf_release_contexts(buf); for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) { + if (buf->buffer->element[i].addr && buf->is_header[i]) + kmem_cache_free(qeth_core_header_cache, + buf->buffer->element[i].addr); + buf->is_header[i] = 0; buf->buffer->element[i].length = 0; buf->buffer->element[i].addr = NULL; buf->buffer->element[i].flags = 0; @@ -3002,8 +3008,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr, if (skb_shinfo(skb)->nr_frags > 0) elements_needed = (skb_shinfo(skb)->nr_frags + 1); if (elements_needed == 0) - elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) - + skb->len) >> PAGE_SHIFT); + elements_needed = 1 + (((((unsigned long) skb->data) % + PAGE_SIZE) + skb->len) >> PAGE_SHIFT); if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) { QETH_DBF_MESSAGE(2, "Invalid size of IP packet " "(Number=%d / Length=%d). Discarded.\n", @@ -3015,7 +3021,8 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr, EXPORT_SYMBOL_GPL(qeth_get_elements_no); static inline void __qeth_fill_buffer(struct sk_buff *skb, - struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill) + struct qdio_buffer *buffer, int is_tso, int *next_element_to_fill, + int offset) { int length = skb->len; int length_here; @@ -3027,6 +3034,11 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb, data = skb->data; first_lap = (is_tso == 0 ? 1 : 0); + if (offset >= 0) { + data = skb->data + offset; + first_lap = 0; + } + while (length > 0) { /* length_here is the remaining amount of data in this page */ length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE); @@ -3058,22 +3070,22 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb, } static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, - struct qeth_qdio_out_buffer *buf, struct sk_buff *skb) + struct qeth_qdio_out_buffer *buf, struct sk_buff *skb, + struct qeth_hdr *hdr, int offset, int hd_len) { struct qdio_buffer *buffer; - struct qeth_hdr_tso *hdr; int flush_cnt = 0, hdr_len, large_send = 0; buffer = buf->buffer; atomic_inc(&skb->users); skb_queue_tail(&buf->skb_list, skb); - hdr = (struct qeth_hdr_tso *) skb->data; /*check first on TSO ....*/ - if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) { + if (hdr->hdr.l3.id == QETH_HEADER_TYPE_TSO) { int element = buf->next_element_to_fill; - hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len; + hdr_len = sizeof(struct qeth_hdr_tso) + + ((struct qeth_hdr_tso *)hdr)->ext.dg_hdr_len; /*fill first buffer entry only with header information */ buffer->element[element].addr = skb->data; buffer->element[element].length = hdr_len; @@ -3083,9 +3095,20 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, skb->len -= hdr_len; large_send = 1; } + + if (offset >= 0) { + int element = buf->next_element_to_fill; + buffer->element[element].addr = hdr; + buffer->element[element].length = sizeof(struct qeth_hdr) + + hd_len; + buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG; + buf->is_header[element] = 1; + buf->next_element_to_fill++; + } + if (skb_shinfo(skb)->nr_frags == 0) __qeth_fill_buffer(skb, buffer, large_send, - (int *)&buf->next_element_to_fill); + (int *)&buf->next_element_to_fill, offset); else __qeth_fill_buffer_frag(skb, buffer, large_send, (int *)&buf->next_element_to_fill); @@ -3115,7 +3138,7 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, int qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue, struct sk_buff *skb, struct qeth_hdr *hdr, int elements_needed, - struct qeth_eddp_context *ctx) + struct qeth_eddp_context *ctx, int offset, int hd_len) { struct qeth_qdio_out_buffer *buffer; int buffers_needed = 0; @@ -3148,7 +3171,7 @@ int qeth_do_send_packet_fast(struct qeth_card *card, } atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED); if (ctx == NULL) { - qeth_fill_buffer(queue, buffer, skb); + qeth_fill_buffer(queue, buffer, skb, hdr, offset, hd_len); qeth_flush_buffers(queue, index, 1); } else { flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index); @@ -3224,7 +3247,7 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue, } } if (ctx == NULL) - tmp = qeth_fill_buffer(queue, buffer, skb); + tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0); else { tmp = qeth_eddp_fill_buffer(queue, ctx, queue->next_buf_to_fill); @@ -4443,8 +4466,17 @@ static int __init qeth_core_init(void) rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; if (rc) goto register_err; - return 0; + qeth_core_header_cache = kmem_cache_create("qeth_hdr", + sizeof(struct qeth_hdr) + ETH_HLEN, 64, 0, NULL); + if (!qeth_core_header_cache) { + rc = -ENOMEM; + goto slab_err; + } + + return 0; +slab_err: + s390_root_dev_unregister(qeth_core_root_dev); register_err: driver_remove_file(&qeth_core_ccwgroup_driver.driver, &driver_attr_group); @@ -4466,6 +4498,7 @@ static void __exit qeth_core_exit(void) &driver_attr_group); ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver); ccw_driver_unregister(&qeth_ccw_driver); + kmem_cache_destroy(qeth_core_header_cache); qeth_unregister_dbf_views(); PRINT_INFO("core functions removed\n"); } diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a8b069cd9a4c..b3cee032f578 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -243,8 +243,7 @@ static void qeth_l2_get_packet_type(struct qeth_card *card, static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct sk_buff *skb, int ipv, int cast_type) { - struct vlan_ethhdr *veth = (struct vlan_ethhdr *)((skb->data) + - QETH_HEADER_SIZE); + struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb_mac_header(skb); memset(hdr, 0, sizeof(struct qeth_hdr)); hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2; @@ -621,6 +620,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) int tx_bytes = skb->len; enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; struct qeth_eddp_context *ctx = NULL; + int data_offset = -1; + int elements_needed = 0; + int hd_len = 0; if ((card->state != CARD_STATE_UP) || !card->lan_online) { card->stats.tx_carrier_errors++; @@ -643,13 +645,32 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (card->info.type == QETH_CARD_TYPE_OSN) hdr = (struct qeth_hdr *)skb->data; else { - /* create a clone with writeable headroom */ - new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr)); - if (!new_skb) - goto tx_drop; - hdr = (struct qeth_hdr *)skb_push(new_skb, + if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && + (skb_shinfo(skb)->nr_frags == 0)) { + new_skb = skb; + data_offset = ETH_HLEN; + hd_len = ETH_HLEN; + hdr = kmem_cache_alloc(qeth_core_header_cache, + GFP_ATOMIC); + if (!hdr) + goto tx_drop; + elements_needed++; + skb_reset_mac_header(new_skb); + qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); + hdr->hdr.l2.pkt_length = new_skb->len; + memcpy(((char *)hdr) + sizeof(struct qeth_hdr), + skb_mac_header(new_skb), ETH_HLEN); + } else { + /* create a clone with writeable headroom */ + new_skb = skb_realloc_headroom(skb, + sizeof(struct qeth_hdr)); + if (!new_skb) + goto tx_drop; + hdr = (struct qeth_hdr *)skb_push(new_skb, sizeof(struct qeth_hdr)); - qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); + skb_set_mac_header(new_skb, sizeof(struct qeth_hdr)); + qeth_l2_fill_header(card, hdr, new_skb, ipv, cast_type); + } } if (large_send == QETH_LARGE_SEND_EDDP) { @@ -660,9 +681,13 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_drop; } } else { - elements = qeth_get_elements_no(card, (void *)hdr, new_skb, 0); - if (!elements) + elements = qeth_get_elements_no(card, (void *)hdr, new_skb, + elements_needed); + if (!elements) { + if (data_offset >= 0) + kmem_cache_free(qeth_core_header_cache, hdr); goto tx_drop; + } } if ((large_send == QETH_LARGE_SEND_NO) && @@ -674,7 +699,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) elements, ctx); else rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - elements, ctx); + elements, ctx, data_offset, hd_len); if (!rc) { card->stats.tx_packets++; card->stats.tx_bytes += tx_bytes; @@ -701,6 +726,9 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (ctx != NULL) qeth_eddp_put_context(ctx); + if (data_offset >= 0) + kmem_cache_free(qeth_core_header_cache, hdr); + if (rc == -EBUSY) { if (new_skb != skb) dev_kfree_skb_any(new_skb); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 3e1d13857350..dd72c3c20165 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2604,6 +2604,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) int tx_bytes = skb->len; enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO; struct qeth_eddp_context *ctx = NULL; + int data_offset = -1; if ((card->info.type == QETH_CARD_TYPE_IQD) && (skb->protocol != htons(ETH_P_IPV6)) && @@ -2624,14 +2625,28 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) card->perf_stats.outbound_start_time = qeth_get_micros(); } - /* create a clone with writeable headroom */ - new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) + - VLAN_HLEN); - if (!new_skb) - goto tx_drop; + if (skb_is_gso(skb)) + large_send = card->options.large_send; + + if ((card->info.type == QETH_CARD_TYPE_IQD) && (!large_send) && + (skb_shinfo(skb)->nr_frags == 0)) { + new_skb = skb; + data_offset = ETH_HLEN; + hdr = kmem_cache_alloc(qeth_core_header_cache, GFP_ATOMIC); + if (!hdr) + goto tx_drop; + elements_needed++; + } else { + /* create a clone with writeable headroom */ + new_skb = skb_realloc_headroom(skb, sizeof(struct qeth_hdr_tso) + + VLAN_HLEN); + if (!new_skb) + goto tx_drop; + } if (card->info.type == QETH_CARD_TYPE_IQD) { - skb_pull(new_skb, ETH_HLEN); + if (data_offset < 0) + skb_pull(new_skb, ETH_HLEN); } else { if (new_skb->protocol == htons(ETH_P_IP)) { if (card->dev->type == ARPHRD_IEEE802_TR) @@ -2657,9 +2672,6 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); - if (skb_is_gso(new_skb)) - large_send = card->options.large_send; - /* fix hardware limitation: as long as we do not have sbal * chaining we can not send long frag lists so we temporary * switch to EDDP @@ -2677,9 +2689,16 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) qeth_tso_fill_header(card, hdr, new_skb); elements_needed++; } else { - hdr = (struct qeth_hdr *)skb_push(new_skb, + if (data_offset < 0) { + hdr = (struct qeth_hdr *)skb_push(new_skb, sizeof(struct qeth_hdr)); - qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); + qeth_l3_fill_header(card, hdr, new_skb, ipv, + cast_type); + } else { + qeth_l3_fill_header(card, hdr, new_skb, ipv, + cast_type); + hdr->hdr.l3.length = new_skb->len - data_offset; + } } if (large_send == QETH_LARGE_SEND_EDDP) { @@ -2695,8 +2714,11 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } else { int elems = qeth_get_elements_no(card, (void *)hdr, new_skb, elements_needed); - if (!elems) + if (!elems) { + if (data_offset >= 0) + kmem_cache_free(qeth_core_header_cache, hdr); goto tx_drop; + } elements_needed += elems; } @@ -2709,7 +2731,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) elements_needed, ctx); else rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - elements_needed, ctx); + elements_needed, ctx, data_offset, 0); if (!rc) { card->stats.tx_packets++; @@ -2737,6 +2759,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (ctx != NULL) qeth_eddp_put_context(ctx); + if (data_offset >= 0) + kmem_cache_free(qeth_core_header_cache, hdr); + if (rc == -EBUSY) { if (new_skb != skb) dev_kfree_skb_any(new_skb); |