Explorar o código

Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6

* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6:
  [S390] qeth: avoid use of include/asm-s390
  [S390] dont use kthread for smp_rescan_cpus().
  [S390] virtio console: fix section mismatch warning.
  [S390] cio: Include linux/string.h in schid.h.
  [S390] qdio: fix section mismatch bug.
  [S390] stp: fix section mismatch warning.
  [S390] Remove diag 0x260 call from memory detection.
  [S390] qdio: make sure qdr is aligned to page size
  [S390] Add support for memory hot-remove.
  [S390] Wire up new syscalls.
  [S390] cio: Memory allocation for idset changed.
  [S390] qeth: preallocated qeth header for hiper socket
  [S390] Optimize storage key operations for anon pages
  [S390] nohz/sclp: disable timer on synchronous waits.
  [S390] ipl: Reboot from alternate device does not work when booting from file
  [S390] dasd: Add support for enhanced VM UID
  [S390] Remove last P390 trace.
Linus Torvalds %!s(int64=17) %!d(string=hai) anos
pai
achega
9fe3b64be3

+ 3 - 0
arch/s390/Kconfig

@@ -317,6 +317,9 @@ config ARCH_ENABLE_MEMORY_HOTPLUG
 	def_bool y
 	def_bool y
 	depends on SPARSEMEM
 	depends on SPARSEMEM
 
 
+config ARCH_ENABLE_MEMORY_HOTREMOVE
+	def_bool y
+
 source "mm/Kconfig"
 source "mm/Kconfig"
 
 
 comment "I/O subsystem configuration"
 comment "I/O subsystem configuration"

+ 37 - 0
arch/s390/kernel/compat_wrapper.S

@@ -1732,3 +1732,40 @@ compat_sys_timerfd_gettime_wrapper:
 	lgfr	%r2,%r2			# int
 	lgfr	%r2,%r2			# int
 	llgtr	%r3,%r3			# struct compat_itimerspec *
 	llgtr	%r3,%r3			# struct compat_itimerspec *
 	jg	compat_sys_timerfd_gettime
 	jg	compat_sys_timerfd_gettime
+
+	.globl compat_sys_signalfd4_wrapper
+compat_sys_signalfd4_wrapper:
+	lgfr	%r2,%r2			# int
+	llgtr	%r3,%r3			# compat_sigset_t *
+	llgfr	%r4,%r4			# compat_size_t
+	lgfr	%r5,%r5			# int
+	jg	compat_sys_signalfd4
+
+	.globl sys_eventfd2_wrapper
+sys_eventfd2_wrapper:
+	llgfr	%r2,%r2			# unsigned int
+	lgfr	%r3,%r3			# int
+	jg	sys_eventfd2
+
+	.globl	sys_inotify_init1_wrapper
+sys_inotify_init1_wrapper:
+	lgfr	%r2,%r2			# int
+	jg	sys_inotify_init1
+
+	.globl	sys_pipe2_wrapper
+sys_pipe2_wrapper:
+	llgtr	%r2,%r2			# u32 *
+	lgfr	%r3,%r3			# int
+	jg	sys_pipe2		# branch to system call
+
+	.globl	sys_dup3_wrapper
+sys_dup3_wrapper:
+	llgfr	%r2,%r2			# unsigned int
+	llgfr	%r3,%r3			# unsigned int
+	lgfr	%r4,%r4			# int
+	jg	sys_dup3		# branch to system call
+
+	.globl	sys_epoll_create1_wrapper
+sys_epoll_create1_wrapper:
+	lgfr	%r2,%r2			# int
+	jg	sys_epoll_create1	# branch to system call

+ 4 - 1
arch/s390/kernel/ipl.c

@@ -1705,7 +1705,10 @@ void __init setup_ipl(void)
 
 
 void __init ipl_update_parameters(void)
 void __init ipl_update_parameters(void)
 {
 {
-	if (diag308(DIAG308_STORE, &ipl_block) == DIAG308_RC_OK)
+	int rc;
+
+	rc = diag308(DIAG308_STORE, &ipl_block);
+	if ((rc == DIAG308_RC_OK) || (rc == DIAG308_RC_NOCONFIG))
 		diag308_set_works = 1;
 		diag308_set_works = 1;
 }
 }
 
 

+ 0 - 23
arch/s390/kernel/mem_detect.c

@@ -9,27 +9,6 @@
 #include <asm/sclp.h>
 #include <asm/sclp.h>
 #include <asm/setup.h>
 #include <asm/setup.h>
 
 
-static int memory_fast_detect(struct mem_chunk *chunk)
-{
-	unsigned long val0 = 0;
-	unsigned long val1 = 0xc;
-	int rc = -EOPNOTSUPP;
-
-	if (ipl_flags & IPL_NSS_VALID)
-		return -EOPNOTSUPP;
-	asm volatile(
-		"	diag	%1,%2,0x260\n"
-		"0:	lhi	%0,0\n"
-		"1:\n"
-		EX_TABLE(0b,1b)
-		: "+d" (rc), "+d" (val0), "+d" (val1) : : "cc");
-
-	if (rc || val0 != val1)
-		return -EOPNOTSUPP;
-	chunk->size = val0 + 1;
-	return 0;
-}
-
 static inline int tprot(unsigned long addr)
 static inline int tprot(unsigned long addr)
 {
 {
 	int rc = -EFAULT;
 	int rc = -EFAULT;
@@ -84,8 +63,6 @@ void detect_memory_layout(struct mem_chunk chunk[])
 	unsigned long flags, cr0;
 	unsigned long flags, cr0;
 
 
 	memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
 	memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
-	if (memory_fast_detect(&chunk[0]) == 0)
-		return;
 	/* Disable IRQs, DAT and low address protection so tprot does the
 	/* Disable IRQs, DAT and low address protection so tprot does the
 	 * right thing and we don't get scheduled away with low address
 	 * right thing and we don't get scheduled away with low address
 	 * protection disabled.
 	 * protection disabled.

+ 6 - 0
arch/s390/kernel/syscalls.S

@@ -330,3 +330,9 @@ SYSCALL(sys_eventfd,sys_eventfd,sys_eventfd_wrapper)
 SYSCALL(sys_timerfd_create,sys_timerfd_create,sys_timerfd_create_wrapper)
 SYSCALL(sys_timerfd_create,sys_timerfd_create,sys_timerfd_create_wrapper)
 SYSCALL(sys_timerfd_settime,sys_timerfd_settime,compat_sys_timerfd_settime_wrapper) /* 320 */
 SYSCALL(sys_timerfd_settime,sys_timerfd_settime,compat_sys_timerfd_settime_wrapper) /* 320 */
 SYSCALL(sys_timerfd_gettime,sys_timerfd_gettime,compat_sys_timerfd_gettime_wrapper)
 SYSCALL(sys_timerfd_gettime,sys_timerfd_gettime,compat_sys_timerfd_gettime_wrapper)
+SYSCALL(sys_signalfd4,sys_signalfd4,compat_sys_signalfd4_wrapper)
+SYSCALL(sys_eventfd2,sys_eventfd2,sys_eventfd2_wrapper)
+SYSCALL(sys_inotify_init1,sys_inotify_init1,sys_inotify_init1_wrapper)
+SYSCALL(sys_pipe2,sys_pipe2,sys_pipe2_wrapper) /* 325 */
+SYSCALL(sys_dup3,sys_dup3,sys_dup3_wrapper)
+SYSCALL(sys_epoll_create1,sys_epoll_create1,sys_epoll_create1_wrapper)

+ 1 - 1
arch/s390/kernel/time.c

@@ -1348,7 +1348,7 @@ early_param("stp", early_parse_stp);
 /*
 /*
  * Reset STP attachment.
  * Reset STP attachment.
  */
  */
-static void stp_reset(void)
+static void __init stp_reset(void)
 {
 {
 	int rc;
 	int rc;
 
 

+ 2 - 2
arch/s390/lib/delay.c

@@ -43,7 +43,7 @@ void __udelay(unsigned long usecs)
 		local_bh_disable();
 		local_bh_disable();
 	local_irq_save(flags);
 	local_irq_save(flags);
 	if (raw_irqs_disabled_flags(flags)) {
 	if (raw_irqs_disabled_flags(flags)) {
-		old_cc = S390_lowcore.clock_comparator;
+		old_cc = local_tick_disable();
 		S390_lowcore.clock_comparator = -1ULL;
 		S390_lowcore.clock_comparator = -1ULL;
 		__ctl_store(cr0, 0, 0);
 		__ctl_store(cr0, 0, 0);
 		dummy = (cr0 & 0xffff00e0) | 0x00000800;
 		dummy = (cr0 & 0xffff00e0) | 0x00000800;
@@ -65,7 +65,7 @@ void __udelay(unsigned long usecs)
 
 
 	if (raw_irqs_disabled_flags(flags)) {
 	if (raw_irqs_disabled_flags(flags)) {
 		__ctl_load(cr0, 0, 0);
 		__ctl_load(cr0, 0, 0);
-		S390_lowcore.clock_comparator = old_cc;
+		local_tick_enable(old_cc);
 	}
 	}
 	if (!irq_context)
 	if (!irq_context)
 		_local_bh_enable();
 		_local_bh_enable();

+ 12 - 1
arch/s390/mm/init.c

@@ -179,7 +179,7 @@ int arch_add_memory(int nid, u64 start, u64 size)
 	int rc;
 	int rc;
 
 
 	pgdat = NODE_DATA(nid);
 	pgdat = NODE_DATA(nid);
-	zone = pgdat->node_zones + ZONE_NORMAL;
+	zone = pgdat->node_zones + ZONE_MOVABLE;
 	rc = vmem_add_mapping(start, size);
 	rc = vmem_add_mapping(start, size);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;
@@ -189,3 +189,14 @@ int arch_add_memory(int nid, u64 start, u64 size)
 	return rc;
 	return rc;
 }
 }
 #endif /* CONFIG_MEMORY_HOTPLUG */
 #endif /* CONFIG_MEMORY_HOTPLUG */
+
+#ifdef CONFIG_MEMORY_HOTREMOVE
+int remove_memory(u64 start, u64 size)
+{
+	unsigned long start_pfn, end_pfn;
+
+	start_pfn = PFN_DOWN(start);
+	end_pfn = start_pfn + PFN_DOWN(size);
+	return offline_pages(start_pfn, end_pfn, 120 * HZ);
+}
+#endif /* CONFIG_MEMORY_HOTREMOVE */

+ 3 - 1
drivers/s390/block/dasd_alias.c

@@ -91,7 +91,8 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
 	else
 	else
 		search_unit_addr = uid->base_unit_addr;
 		search_unit_addr = uid->base_unit_addr;
 	list_for_each_entry(pos, &lcu->grouplist, group) {
 	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 pos;
 	};
 	};
 	return NULL;
 	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;
 			group->uid.base_unit_addr = uid->real_unit_addr;
 		else
 		else
 			group->uid.base_unit_addr = uid->base_unit_addr;
 			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->group);
 		INIT_LIST_HEAD(&group->baselist);
 		INIT_LIST_HEAD(&group->baselist);
 		INIT_LIST_HEAD(&group->aliaslist);
 		INIT_LIST_HEAD(&group->aliaslist);

+ 13 - 3
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);
 static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);
 
 
 #define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial    */ 14 + 1 +\
 #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
 static ssize_t
 dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
 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);
 		sprintf(ua_string, "%02x", uid->real_unit_addr);
 		break;
 		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);
 	spin_unlock(&dasd_devmap_lock);
 	return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
 	return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
 }
 }

+ 112 - 35
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));
 	memset(pfxdata, 0, sizeof(*pfxdata));
 	/* prefix data */
 	/* prefix data */
 	pfxdata->format = 0;
 	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;
 	pfxdata->validity.define_extend = 1;
 
 
 	/* private uid is kept up to date, conf_data may be outdated */
 	/* 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.
  * 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_private *private;
-	struct dasd_eckd_confdata *confdata;
+	int count;
 
 
 	private = (struct dasd_eckd_private *) device->private;
 	private = (struct dasd_eckd_private *) device->private;
 	if (!private)
 	if (!private)
 		return -ENODEV;
 		return -ENODEV;
-	confdata = &private->conf_data;
-	if (!confdata)
+	if (!private->ned || !private->gneq)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	memset(uid, 0, sizeof(struct dasd_uid));
 	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);
 	       sizeof(uid->vendor) - 1);
 	EBCASC(uid->vendor, 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);
 	       sizeof(uid->serial) - 1);
 	EBCASC(uid->serial, 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)
 		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 {
 	} else {
 		uid->type = UA_BASE_DEVICE;
 		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;
 	return 0;
 }
 }
 
 
@@ -623,6 +627,15 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto out_error;
 		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);
 	cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
 	if (IS_ERR(cqr)) {
 	if (IS_ERR(cqr)) {
 		ret =  PTR_ERR(cqr);
 		ret =  PTR_ERR(cqr);
@@ -646,8 +659,62 @@ out_error:
 	return ret;
 	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;
 	void *conf_data;
 	int conf_len, conf_data_saved;
 	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);
 	path_data->opm = ccw_device_get_path_mask(device->cdev);
 	lpm = 0x80;
 	lpm = 0x80;
 	conf_data_saved = 0;
 	conf_data_saved = 0;
-
 	/* get configuration data per operational path */
 	/* get configuration data per operational path */
 	for (lpm = 0x80; lpm; lpm>>= 1) {
 	for (lpm = 0x80; lpm; lpm>>= 1) {
 		if (lpm & path_data->opm){
 		if (lpm & path_data->opm){
@@ -678,22 +744,20 @@ dasd_eckd_read_conf(struct dasd_device *device)
 					"data retrieved");
 					"data retrieved");
 				continue;	/* no error */
 				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 */
 			/* 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++;
 				conf_data_saved++;
 			}
 			}
-			switch (((char *)conf_data)[242] & 0x07){
+			switch (dasd_eckd_path_access(conf_data, conf_len)) {
 			case 0x02:
 			case 0x02:
 				path_data->npm |= lpm;
 				path_data->npm |= lpm;
 				break;
 				break;
@@ -701,7 +765,8 @@ dasd_eckd_read_conf(struct dasd_device *device)
 				path_data->ppm |= lpm;
 				path_data->ppm |= lpm;
 				break;
 				break;
 			}
 			}
-			kfree(conf_data);
+			if (conf_data != private->conf_data)
+				kfree(conf_data);
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
@@ -952,6 +1017,7 @@ out_err2:
 	dasd_free_block(device->block);
 	dasd_free_block(device->block);
 	device->block = NULL;
 	device->block = NULL;
 out_err1:
 out_err1:
+	kfree(private->conf_data);
 	kfree(device->private);
 	kfree(device->private);
 	device->private = NULL;
 	device->private = NULL;
 	return rc;
 	return rc;
@@ -959,7 +1025,17 @@ out_err1:
 
 
 static void dasd_eckd_uncheck_device(struct dasd_device *device)
 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);
 	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 *
 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);
 	info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
 	memcpy(info->characteristics, &private->rdc_data,
 	memcpy(info->characteristics, &private->rdc_data,
 	       sizeof(struct dasd_eckd_characteristics));
 	       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;
 	return 0;
 }
 }
 
 

+ 60 - 124
drivers/s390/block/dasd_eckd.h

@@ -231,133 +231,62 @@ struct dasd_eckd_characteristics {
 	__u8 reserved3[10];
 	__u8 reserved3[10];
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
-struct dasd_eckd_confdata {
+/* elements of the configuration data */
+struct dasd_ned {
 	struct {
 	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 {
-		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 {
-		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 {
-		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));
 } __attribute__ ((packed));
 
 
 struct dasd_eckd_path {
 struct dasd_eckd_path {
@@ -463,7 +392,14 @@ struct alias_pav_group {
 
 
 struct dasd_eckd_private {
 struct dasd_eckd_private {
 	struct dasd_eckd_characteristics rdc_data;
 	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 dasd_eckd_path path_data;
 	struct eckd_count count_area[5];
 	struct eckd_count count_area[5];
 	int init_cqr_status;
 	int init_cqr_status;

+ 1 - 0
drivers/s390/block/dasd_int.h

@@ -307,6 +307,7 @@ struct dasd_uid {
 	__u16 ssid;
 	__u16 ssid;
 	__u8 real_unit_addr;
 	__u8 real_unit_addr;
 	__u8 base_unit_addr;
 	__u8 base_unit_addr;
+	char vduit[33];
 };
 };
 
 
 /*
 /*

+ 4 - 2
drivers/s390/char/sclp.c

@@ -399,6 +399,7 @@ sclp_tod_from_jiffies(unsigned long jiffies)
 void
 void
 sclp_sync_wait(void)
 sclp_sync_wait(void)
 {
 {
+	unsigned long long old_tick;
 	unsigned long flags;
 	unsigned long flags;
 	unsigned long cr0, cr0_sync;
 	unsigned long cr0, cr0_sync;
 	u64 timeout;
 	u64 timeout;
@@ -419,11 +420,12 @@ sclp_sync_wait(void)
 	if (!irq_context)
 	if (!irq_context)
 		local_bh_disable();
 		local_bh_disable();
 	/* Enable service-signal interruption, disable timer interrupts */
 	/* Enable service-signal interruption, disable timer interrupts */
+	old_tick = local_tick_disable();
 	trace_hardirqs_on();
 	trace_hardirqs_on();
 	__ctl_store(cr0, 0, 0);
 	__ctl_store(cr0, 0, 0);
 	cr0_sync = cr0;
 	cr0_sync = cr0;
+	cr0_sync &= 0xffff00a0;
 	cr0_sync |= 0x00000200;
 	cr0_sync |= 0x00000200;
-	cr0_sync &= 0xFFFFF3AC;
 	__ctl_load(cr0_sync, 0, 0);
 	__ctl_load(cr0_sync, 0, 0);
 	__raw_local_irq_stosm(0x01);
 	__raw_local_irq_stosm(0x01);
 	/* Loop until driver state indicates finished request */
 	/* Loop until driver state indicates finished request */
@@ -439,9 +441,9 @@ sclp_sync_wait(void)
 	__ctl_load(cr0, 0, 0);
 	__ctl_load(cr0, 0, 0);
 	if (!irq_context)
 	if (!irq_context)
 		_local_bh_enable();
 		_local_bh_enable();
+	local_tick_enable(old_tick);
 	local_irq_restore(flags);
 	local_irq_restore(flags);
 }
 }
-
 EXPORT_SYMBOL(sclp_sync_wait);
 EXPORT_SYMBOL(sclp_sync_wait);
 
 
 /* Dispatch changes in send and receive mask to registered listeners. */
 /* Dispatch changes in send and receive mask to registered listeners. */

+ 5 - 0
drivers/s390/char/sclp_cmd.c

@@ -427,6 +427,8 @@ static int sclp_mem_notifier(struct notifier_block *nb,
 			sclp_attach_storage(id);
 			sclp_attach_storage(id);
 	switch (action) {
 	switch (action) {
 	case MEM_ONLINE:
 	case MEM_ONLINE:
+	case MEM_GOING_OFFLINE:
+	case MEM_CANCEL_OFFLINE:
 		break;
 		break;
 	case MEM_GOING_ONLINE:
 	case MEM_GOING_ONLINE:
 		rc = sclp_mem_change_state(start, size, 1);
 		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:
 	case MEM_CANCEL_ONLINE:
 		sclp_mem_change_state(start, size, 0);
 		sclp_mem_change_state(start, size, 0);
 		break;
 		break;
+	case MEM_OFFLINE:
+		sclp_mem_change_state(start, size, 0);
+		break;
 	default:
 	default:
 		rc = -EINVAL;
 		rc = -EINVAL;
 		break;
 		break;

+ 1 - 12
drivers/s390/char/sclp_config.c

@@ -8,7 +8,6 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/cpu.h>
 #include <linux/cpu.h>
-#include <linux/kthread.h>
 #include <linux/sysdev.h>
 #include <linux/sysdev.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 #include <asm/smp.h>
 #include <asm/smp.h>
@@ -41,19 +40,9 @@ static void sclp_cpu_capability_notify(struct work_struct *work)
 	put_online_cpus();
 	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)
 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)
 static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)

+ 4 - 4
drivers/s390/cio/idset.c

@@ -5,7 +5,7 @@
  *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
  *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
  */
  */
 
 
-#include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include "idset.h"
 #include "idset.h"
 #include "css.h"
 #include "css.h"
@@ -25,18 +25,18 @@ static struct idset *idset_new(int num_ssid, int num_id)
 {
 {
 	struct idset *set;
 	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) {
 	if (set) {
 		set->num_ssid = num_ssid;
 		set->num_ssid = num_ssid;
 		set->num_id = num_id;
 		set->num_id = num_id;
+		memset(set->bitmap, 0, bitmap_size(num_ssid, num_id));
 	}
 	}
 	return set;
 	return set;
 }
 }
 
 
 void idset_free(struct idset *set)
 void idset_free(struct idset *set)
 {
 {
-	kfree(set);
+	vfree(set);
 }
 }
 
 
 void idset_clear(struct idset *set)
 void idset_clear(struct idset *set)

+ 1 - 1
drivers/s390/cio/qdio_main.c

@@ -1355,7 +1355,7 @@ int qdio_allocate(struct qdio_initialize *init_data)
 		goto out_rel;
 		goto out_rel;
 
 
 	/* qdr is used in ccw1.cda which is u32 */
 	/* 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)
 	if (!irq_ptr->qdr)
 		goto out_rel;
 		goto out_rel;
 	WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
 	WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);

+ 1 - 1
drivers/s390/cio/qdio_perf.c

@@ -142,7 +142,7 @@ int __init qdio_setup_perf_stats(void)
 	return 0;
 	return 0;
 }
 }
 
 
-void __exit qdio_remove_perf_stats(void)
+void qdio_remove_perf_stats(void)
 {
 {
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_PROC_FS
 	remove_proc_entry("qdio_perf", NULL);
 	remove_proc_entry("qdio_perf", NULL);

+ 2 - 2
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);
 			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(irq_ptr->chsc_page);
 	free_page((unsigned long) irq_ptr);
 	free_page((unsigned long) irq_ptr);
 }
 }
@@ -515,7 +515,7 @@ int __init qdio_setup_init(void)
 	return 0;
 	return 0;
 }
 }
 
 
-void __exit qdio_setup_exit(void)
+void qdio_setup_exit(void)
 {
 {
 	kmem_cache_destroy(qdio_q_cache);
 	kmem_cache_destroy(qdio_q_cache);
 }
 }

+ 1 - 1
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;
 	return len;
 }
 }
 
 
-void s390_virtio_console_init(void)
+void __init s390_virtio_console_init(void)
 {
 {
 	virtio_cons_early_init(early_put_chars);
 	virtio_cons_early_init(early_put_chars);
 }
 }

+ 3 - 2
drivers/s390/net/qeth_core.h

@@ -419,6 +419,7 @@ struct qeth_qdio_out_buffer {
 	int next_element_to_fill;
 	int next_element_to_fill;
 	struct sk_buff_head skb_list;
 	struct sk_buff_head skb_list;
 	struct list_head ctx_list;
 	struct list_head ctx_list;
+	int is_header[16];
 };
 };
 
 
 struct qeth_card;
 struct qeth_card;
@@ -785,7 +786,7 @@ void qeth_core_remove_osn_attributes(struct device *);
 
 
 /* exports for qeth discipline device drivers */
 /* exports for qeth discipline device drivers */
 extern struct qeth_card_list_struct qeth_core_card_list;
 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];
 extern struct qeth_dbf_info qeth_dbf[QETH_DBF_INFOS];
 
 
 void qeth_set_allowed_threads(struct qeth_card *, unsigned long , int);
 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_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 *,
 int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *,
 			struct sk_buff *, struct qeth_hdr *, int,
 			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 *,
 int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *,
 		    struct sk_buff *, struct qeth_hdr *,
 		    struct sk_buff *, struct qeth_hdr *,
 		    int, struct qeth_eddp_context *);
 		    int, struct qeth_eddp_context *);

+ 48 - 15
drivers/s390/net/qeth_core_main.c

@@ -19,8 +19,8 @@
 #include <linux/mii.h>
 #include <linux/mii.h>
 #include <linux/kthread.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 <asm/s390_rdev.h>
 
 
 #include "qeth_core.h"
 #include "qeth_core.h"
@@ -48,6 +48,8 @@ EXPORT_SYMBOL_GPL(qeth_dbf);
 
 
 struct qeth_card_list_struct qeth_core_card_list;
 struct qeth_card_list_struct qeth_core_card_list;
 EXPORT_SYMBOL_GPL(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 struct device *qeth_core_root_dev;
 static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
 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);
 	qeth_eddp_buf_release_contexts(buf);
 	for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i) {
 	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].length = 0;
 		buf->buffer->element[i].addr = NULL;
 		buf->buffer->element[i].addr = NULL;
 		buf->buffer->element[i].flags = 0;
 		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)
 	if (skb_shinfo(skb)->nr_frags > 0)
 		elements_needed = (skb_shinfo(skb)->nr_frags + 1);
 		elements_needed = (skb_shinfo(skb)->nr_frags + 1);
 	if (elements_needed == 0)
 	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)) {
 	if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
 		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
 		QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
 			"(Number=%d / Length=%d). Discarded.\n",
 			"(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);
 EXPORT_SYMBOL_GPL(qeth_get_elements_no);
 
 
 static inline void __qeth_fill_buffer(struct sk_buff *skb,
 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 = skb->len;
 	int length_here;
 	int length_here;
@@ -3027,6 +3034,11 @@ static inline void __qeth_fill_buffer(struct sk_buff *skb,
 	data = skb->data;
 	data = skb->data;
 	first_lap = (is_tso == 0 ? 1 : 0);
 	first_lap = (is_tso == 0 ? 1 : 0);
 
 
+	if (offset >= 0) {
+		data = skb->data + offset;
+		first_lap = 0;
+	}
+
 	while (length > 0) {
 	while (length > 0) {
 		/* length_here is the remaining amount of data in this page */
 		/* length_here is the remaining amount of data in this page */
 		length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
 		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,
 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 qdio_buffer *buffer;
-	struct qeth_hdr_tso *hdr;
 	int flush_cnt = 0, hdr_len, large_send = 0;
 	int flush_cnt = 0, hdr_len, large_send = 0;
 
 
 	buffer = buf->buffer;
 	buffer = buf->buffer;
 	atomic_inc(&skb->users);
 	atomic_inc(&skb->users);
 	skb_queue_tail(&buf->skb_list, skb);
 	skb_queue_tail(&buf->skb_list, skb);
 
 
-	hdr  = (struct qeth_hdr_tso *) skb->data;
 	/*check first on TSO ....*/
 	/*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;
 		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 */
 		/*fill first buffer entry only with header information */
 		buffer->element[element].addr = skb->data;
 		buffer->element[element].addr = skb->data;
 		buffer->element[element].length = hdr_len;
 		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;
 		skb->len  -= hdr_len;
 		large_send = 1;
 		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)
 	if (skb_shinfo(skb)->nr_frags == 0)
 		__qeth_fill_buffer(skb, buffer, large_send,
 		__qeth_fill_buffer(skb, buffer, large_send,
-				   (int *)&buf->next_element_to_fill);
+				(int *)&buf->next_element_to_fill, offset);
 	else
 	else
 		__qeth_fill_buffer_frag(skb, buffer, large_send,
 		__qeth_fill_buffer_frag(skb, buffer, large_send,
 					(int *)&buf->next_element_to_fill);
 					(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,
 int qeth_do_send_packet_fast(struct qeth_card *card,
 		struct qeth_qdio_out_q *queue, struct sk_buff *skb,
 		struct qeth_qdio_out_q *queue, struct sk_buff *skb,
 		struct qeth_hdr *hdr, int elements_needed,
 		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;
 	struct qeth_qdio_out_buffer *buffer;
 	int buffers_needed = 0;
 	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);
 	atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
 	if (ctx == NULL) {
 	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);
 		qeth_flush_buffers(queue, index, 1);
 	} else {
 	} else {
 		flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
 		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)
 	if (ctx == NULL)
-		tmp = qeth_fill_buffer(queue, buffer, skb);
+		tmp = qeth_fill_buffer(queue, buffer, skb, hdr, -1, 0);
 	else {
 	else {
 		tmp = qeth_eddp_fill_buffer(queue, ctx,
 		tmp = qeth_eddp_fill_buffer(queue, ctx,
 						queue->next_buf_to_fill);
 						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;
 	rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0;
 	if (rc)
 	if (rc)
 		goto register_err;
 		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:
 register_err:
 	driver_remove_file(&qeth_core_ccwgroup_driver.driver,
 	driver_remove_file(&qeth_core_ccwgroup_driver.driver,
 			   &driver_attr_group);
 			   &driver_attr_group);
@@ -4466,6 +4498,7 @@ static void __exit qeth_core_exit(void)
 			   &driver_attr_group);
 			   &driver_attr_group);
 	ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
 	ccwgroup_driver_unregister(&qeth_core_ccwgroup_driver);
 	ccw_driver_unregister(&qeth_ccw_driver);
 	ccw_driver_unregister(&qeth_ccw_driver);
+	kmem_cache_destroy(qeth_core_header_cache);
 	qeth_unregister_dbf_views();
 	qeth_unregister_dbf_views();
 	PRINT_INFO("core functions removed\n");
 	PRINT_INFO("core functions removed\n");
 }
 }

+ 39 - 11
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,
 static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
 			struct sk_buff *skb, int ipv, int cast_type)
 			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));
 	memset(hdr, 0, sizeof(struct qeth_hdr));
 	hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
 	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;
 	int tx_bytes = skb->len;
 	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
 	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
 	struct qeth_eddp_context *ctx = NULL;
 	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) {
 	if ((card->state != CARD_STATE_UP) || !card->lan_online) {
 		card->stats.tx_carrier_errors++;
 		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)
 	if (card->info.type == QETH_CARD_TYPE_OSN)
 		hdr = (struct qeth_hdr *)skb->data;
 		hdr = (struct qeth_hdr *)skb->data;
 	else {
 	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));
 						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) {
 	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;
 			goto tx_drop;
 		}
 		}
 	} else {
 	} 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;
 			goto tx_drop;
+		}
 	}
 	}
 
 
 	if ((large_send == QETH_LARGE_SEND_NO) &&
 	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);
 					 elements, ctx);
 	else
 	else
 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
-					      elements, ctx);
+					elements, ctx, data_offset, hd_len);
 	if (!rc) {
 	if (!rc) {
 		card->stats.tx_packets++;
 		card->stats.tx_packets++;
 		card->stats.tx_bytes += tx_bytes;
 		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)
 		if (ctx != NULL)
 			qeth_eddp_put_context(ctx);
 			qeth_eddp_put_context(ctx);
 
 
+		if (data_offset >= 0)
+			kmem_cache_free(qeth_core_header_cache, hdr);
+
 		if (rc == -EBUSY) {
 		if (rc == -EBUSY) {
 			if (new_skb != skb)
 			if (new_skb != skb)
 				dev_kfree_skb_any(new_skb);
 				dev_kfree_skb_any(new_skb);

+ 38 - 13
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;
 	int tx_bytes = skb->len;
 	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
 	enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
 	struct qeth_eddp_context *ctx = NULL;
 	struct qeth_eddp_context *ctx = NULL;
+	int data_offset = -1;
 
 
 	if ((card->info.type == QETH_CARD_TYPE_IQD) &&
 	if ((card->info.type == QETH_CARD_TYPE_IQD) &&
 	    (skb->protocol != htons(ETH_P_IPV6)) &&
 	    (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();
 		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) {
 	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 {
 	} else {
 		if (new_skb->protocol == htons(ETH_P_IP)) {
 		if (new_skb->protocol == htons(ETH_P_IP)) {
 			if (card->dev->type == ARPHRD_IEEE802_TR)
 			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);
 	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
 	/* fix hardware limitation: as long as we do not have sbal
 	 * chaining we can not send long frag lists so we temporary
 	 * chaining we can not send long frag lists so we temporary
 	 * switch to EDDP
 	 * 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);
 		qeth_tso_fill_header(card, hdr, new_skb);
 		elements_needed++;
 		elements_needed++;
 	} else {
 	} 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));
 						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) {
 	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 {
 	} else {
 		int elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
 		int elems = qeth_get_elements_no(card, (void *)hdr, new_skb,
 						 elements_needed);
 						 elements_needed);
-		if (!elems)
+		if (!elems) {
+			if (data_offset >= 0)
+				kmem_cache_free(qeth_core_header_cache, hdr);
 			goto tx_drop;
 			goto tx_drop;
+		}
 		elements_needed += elems;
 		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);
 					 elements_needed, ctx);
 	else
 	else
 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
 		rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr,
-					      elements_needed, ctx);
+					elements_needed, ctx, data_offset, 0);
 
 
 	if (!rc) {
 	if (!rc) {
 		card->stats.tx_packets++;
 		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)
 		if (ctx != NULL)
 			qeth_eddp_put_context(ctx);
 			qeth_eddp_put_context(ctx);
 
 
+		if (data_offset >= 0)
+			kmem_cache_free(qeth_core_header_cache, hdr);
+
 		if (rc == -EBUSY) {
 		if (rc == -EBUSY) {
 			if (new_skb != skb)
 			if (new_skb != skb)
 				dev_kfree_skb_any(new_skb);
 				dev_kfree_skb_any(new_skb);

+ 14 - 0
include/asm-s390/hardirq.h

@@ -34,4 +34,18 @@ typedef struct {
 
 
 void clock_comparator_work(void);
 void clock_comparator_work(void);
 
 
+static inline unsigned long long local_tick_disable(void)
+{
+	unsigned long long old;
+
+	old = S390_lowcore.clock_comparator;
+	S390_lowcore.clock_comparator = -1ULL;
+	return old;
+}
+
+static inline void local_tick_enable(unsigned long long comp)
+{
+	S390_lowcore.clock_comparator = comp;
+}
+
 #endif /* __ASM_HARDIRQ_H */
 #endif /* __ASM_HARDIRQ_H */

+ 2 - 1
include/asm-s390/ipl.h

@@ -159,7 +159,8 @@ enum diag308_vm_flags {
 };
 };
 
 
 enum diag308_rc {
 enum diag308_rc {
-	DIAG308_RC_OK	= 1,
+	DIAG308_RC_OK		= 0x0001,
+	DIAG308_RC_NOCONFIG	= 0x0102,
 };
 };
 
 
 extern int diag308(unsigned long subcode, void *addr);
 extern int diag308(unsigned long subcode, void *addr);

+ 1 - 0
include/asm-s390/schid.h

@@ -11,6 +11,7 @@ struct subchannel_id {
 } __attribute__ ((packed, aligned(4)));
 } __attribute__ ((packed, aligned(4)));
 
 
 #ifdef __KERNEL__
 #ifdef __KERNEL__
+#include <linux/string.h>
 
 
 /* Helper function for sane state of pre-allocated subchannel_id. */
 /* Helper function for sane state of pre-allocated subchannel_id. */
 static inline void
 static inline void

+ 0 - 1
include/asm-s390/setup.h

@@ -65,7 +65,6 @@ extern unsigned long machine_flags;
 
 
 #define MACHINE_FLAG_VM		(1UL << 0)
 #define MACHINE_FLAG_VM		(1UL << 0)
 #define MACHINE_FLAG_IEEE	(1UL << 1)
 #define MACHINE_FLAG_IEEE	(1UL << 1)
-#define MACHINE_FLAG_P390	(1UL << 2)
 #define MACHINE_FLAG_CSP	(1UL << 3)
 #define MACHINE_FLAG_CSP	(1UL << 3)
 #define MACHINE_FLAG_MVPG	(1UL << 4)
 #define MACHINE_FLAG_MVPG	(1UL << 4)
 #define MACHINE_FLAG_DIAG44	(1UL << 5)
 #define MACHINE_FLAG_DIAG44	(1UL << 5)

+ 7 - 1
include/asm-s390/unistd.h

@@ -259,7 +259,13 @@
 #define __NR_timerfd_create	319
 #define __NR_timerfd_create	319
 #define __NR_timerfd_settime	320
 #define __NR_timerfd_settime	320
 #define __NR_timerfd_gettime	321
 #define __NR_timerfd_gettime	321
-#define NR_syscalls 322
+#define __NR_signalfd4		322
+#define __NR_eventfd2		323
+#define __NR_inotify_init1	324
+#define __NR_pipe2		325
+#define __NR_dup3		326
+#define __NR_epoll_create1	327
+#define NR_syscalls 328
 
 
 /* 
 /* 
  * There are some system calls that are not present on 64 bit, some
  * There are some system calls that are not present on 64 bit, some

+ 0 - 3
include/linux/page-flags.h

@@ -239,9 +239,6 @@ static inline void __SetPageUptodate(struct page *page)
 {
 {
 	smp_wmb();
 	smp_wmb();
 	__set_bit(PG_uptodate, &(page)->flags);
 	__set_bit(PG_uptodate, &(page)->flags);
-#ifdef CONFIG_S390
-	page_clear_dirty(page);
-#endif
 }
 }
 
 
 static inline void SetPageUptodate(struct page *page)
 static inline void SetPageUptodate(struct page *page)

+ 2 - 1
mm/rmap.c

@@ -667,7 +667,8 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma)
 		 * Leaving it set also helps swapoff to reinstate ptes
 		 * Leaving it set also helps swapoff to reinstate ptes
 		 * faster for those pages still in swapcache.
 		 * faster for those pages still in swapcache.
 		 */
 		 */
-		if (page_test_dirty(page)) {
+		if ((!PageAnon(page) || PageSwapCache(page)) &&
+		    page_test_dirty(page)) {
 			page_clear_dirty(page);
 			page_clear_dirty(page);
 			set_page_dirty(page);
 			set_page_dirty(page);
 		}
 		}