diff options
Diffstat (limited to 'drivers/s390/char')
52 files changed, 2440 insertions, 2460 deletions
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 40834f18754..71bf959732f 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -2,76 +2,85 @@ comment "S/390 character device drivers"  	depends on S390  config TN3270 -	tristate "Support for locally attached 3270 terminals" +	def_tristate y +	prompt "Support for locally attached 3270 terminals"  	depends on CCW  	help  	  Include support for IBM 3270 terminals.  config TN3270_TTY -	tristate "Support for tty input/output on 3270 terminals" -	depends on TN3270 +	def_tristate y +	prompt "Support for tty input/output on 3270 terminals" +	depends on TN3270 && TTY  	help  	  Include support for using an IBM 3270 terminal as a Linux tty.  config TN3270_FS -	tristate "Support for fullscreen applications on 3270 terminals" +	def_tristate m +	prompt "Support for fullscreen applications on 3270 terminals"  	depends on TN3270  	help  	  Include support for fullscreen applications on an IBM 3270 terminal.  config TN3270_CONSOLE -	bool "Support for console on 3270 terminal" +	def_bool y +	prompt "Support for console on 3270 terminal"  	depends on TN3270=y && TN3270_TTY=y  	help  	  Include support for using an IBM 3270 terminal as a Linux system  	  console.  Available only if 3270 support is compiled in statically.  config TN3215 -	bool "Support for 3215 line mode terminal" -	depends on CCW +	def_bool y +	prompt "Support for 3215 line mode terminal" +	depends on CCW && TTY  	help  	  Include support for IBM 3215 line-mode terminals.  config TN3215_CONSOLE -	bool "Support for console on 3215 line mode terminal" +	def_bool y +	prompt "Support for console on 3215 line mode terminal"  	depends on TN3215  	help  	  Include support for using an IBM 3215 line-mode terminal as a  	  Linux system console.  config CCW_CONSOLE -	bool -	depends on TN3215_CONSOLE || TN3270_CONSOLE -	default y +	def_bool y if TN3215_CONSOLE || TN3270_CONSOLE  config SCLP_TTY -	bool "Support for SCLP line mode terminal" -	depends on S390 +	def_bool y +	prompt "Support for SCLP line mode terminal" +	depends on S390 && TTY  	help  	  Include support for IBM SCLP line-mode terminals.  config SCLP_CONSOLE -	bool "Support for console on SCLP line mode terminal" +	def_bool y +	prompt "Support for console on SCLP line mode terminal"  	depends on SCLP_TTY  	help  	  Include support for using an IBM HWC line-mode terminal as the Linux  	  system console.  config SCLP_VT220_TTY -	bool "Support for SCLP VT220-compatible terminal" -	depends on S390 +	def_bool y +	prompt "Support for SCLP VT220-compatible terminal" +	depends on S390 && TTY  	help  	  Include support for an IBM SCLP VT220-compatible terminal.  config SCLP_VT220_CONSOLE -	bool "Support for console on SCLP VT220-compatible terminal" +	def_bool y +	prompt "Support for console on SCLP VT220-compatible terminal"  	depends on SCLP_VT220_TTY  	help  	  Include support for using an IBM SCLP VT220-compatible terminal as a  	  Linux system console.  config SCLP_CPI -	tristate "Control-Program Identification" +	def_tristate m +	prompt "Control-Program Identification"  	depends on S390  	help  	  This option enables the hardware console interface for system @@ -83,7 +92,8 @@ config SCLP_CPI  	  need this feature and intend to run your kernel in LPAR.  config SCLP_ASYNC -	tristate "Support for Call Home via Asynchronous SCLP Records" +	def_tristate m +	prompt "Support for Call Home via Asynchronous SCLP Records"  	depends on S390  	help  	  This option enables the call home function, which is able to inform @@ -93,7 +103,8 @@ config SCLP_ASYNC  	  need this feature and intend to run your kernel in LPAR.  config S390_TAPE -	tristate "S/390 tape device support" +	def_tristate m +	prompt "S/390 tape device support"  	depends on CCW  	help  	  Select this option if you want to access channel-attached tape @@ -105,25 +116,12 @@ config S390_TAPE  	  called tape390 and include all selected interfaces and  	  hardware drivers. -comment "S/390 tape interface support" -	depends on S390_TAPE - -config S390_TAPE_BLOCK -	bool "Support for tape block devices" -	depends on S390_TAPE && BLOCK -	help -	  Select this option if you want to access your channel-attached tape -	  devices using the block device interface.  This interface is similar -	  to CD-ROM devices on other platforms.  The tapes can only be -	  accessed read-only when using this interface.  Have a look at -	  <file:Documentation/s390/TAPE> for further information about creating -	  volumes for and using this interface.  It is safe to say "Y" here. -  comment "S/390 tape hardware support"  	depends on S390_TAPE  config S390_TAPE_34XX -	tristate "Support for 3480/3490 tape hardware" +	def_tristate m +	prompt "Support for 3480/3490 tape hardware"  	depends on S390_TAPE  	help  	  Select this option if you want to access IBM 3480/3490 magnetic @@ -131,7 +129,8 @@ config S390_TAPE_34XX  	  It is safe to say "Y" here.  config S390_TAPE_3590 -	tristate "Support for 3590 tape hardware" +	def_tristate m +	prompt "Support for 3590 tape hardware"  	depends on S390_TAPE  	help  	  Select this option if you want to access IBM 3590 magnetic @@ -139,7 +138,8 @@ config S390_TAPE_3590  	  It is safe to say "Y" here.  config VMLOGRDR -	tristate "Support for the z/VM recording system services (VM only)" +	def_tristate m +	prompt "Support for the z/VM recording system services (VM only)"  	depends on IUCV  	help  	  Select this option if you want to be able to receive records collected @@ -148,29 +148,31 @@ config VMLOGRDR  	  This driver depends on the IUCV support driver.  config VMCP -	bool "Support for the z/VM CP interface" +	def_bool y +	prompt "Support for the z/VM CP interface"  	depends on S390  	help  	  Select this option if you want to be able to interact with the control  	  program on z/VM  config MONREADER -	tristate "API for reading z/VM monitor service records" +	def_tristate m +	prompt "API for reading z/VM monitor service records"  	depends on IUCV  	help  	  Character device driver for reading z/VM monitor service records  config MONWRITER -	tristate "API for writing z/VM monitor service records" +	def_tristate m +	prompt "API for writing z/VM monitor service records"  	depends on S390 -	default "m"  	help  	  Character device driver for writing z/VM monitor service records  config S390_VMUR -	tristate "z/VM unit record device driver" +	def_tristate m +	prompt "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 efb500ab66c..78b6ace7edc 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,8 @@  #  obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ -	 sclp_cmd.o sclp_config.o sclp_cpi_sys.o +	 sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ +	 sclp_early.o  obj-$(CONFIG_TN3270) += raw3270.o  obj-$(CONFIG_TN3270_CONSOLE) += con3270.o @@ -18,11 +19,9 @@ obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o  obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o  obj-$(CONFIG_SCLP_ASYNC) += sclp_async.o -obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o  obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o  obj-$(CONFIG_VMCP) += vmcp.o -tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o  tape-$(CONFIG_PROC_FS) += tape_proc.o  tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)  obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o @@ -33,4 +32,4 @@ 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 +obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 59ec073724b..5af7f0bd612 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -20,6 +20,7 @@  #include <linux/interrupt.h>  #include <linux/err.h>  #include <linux/reboot.h> +#include <linux/serial.h> /* ASYNC_* flags */  #include <linux/slab.h>  #include <asm/ccwdev.h>  #include <asm/cio.h> @@ -44,14 +45,11 @@  #define RAW3215_TIMEOUT	    HZ/10     /* time for delayed output */  #define RAW3215_FIXED	    1	      /* 3215 console device is not be freed */ -#define RAW3215_ACTIVE	    2	      /* set if the device is in use */  #define RAW3215_WORKING	    4	      /* set if a request is being worked on */  #define RAW3215_THROTTLED   8	      /* set if reading is disabled */  #define RAW3215_STOPPED	    16	      /* set if writing is disabled */ -#define RAW3215_CLOSING	    32	      /* set while in close process */  #define RAW3215_TIMER_RUNS  64	      /* set if the output delay timer is on */  #define RAW3215_FLUSHING    128	      /* set to flush buffer (no delay) */ -#define RAW3215_FROZEN	    256	      /* set if 3215 is frozen for suspend */  #define TAB_STOP_SIZE	    8	      /* tab stop size */ @@ -76,6 +74,7 @@ struct raw3215_req {  } __attribute__ ((aligned(8)));  struct raw3215_info { +	struct tty_port port;  	struct ccw_device *cdev;      /* device for tty driver */  	spinlock_t *lock;	      /* pointer to irq lock */  	int flags;		      /* state flags */ @@ -84,9 +83,9 @@ struct raw3215_info {  	int head;		      /* first free byte in output buffer */  	int count;		      /* number of bytes in output buffer */  	int written;		      /* number of bytes in write requests */ -	struct tty_struct *tty;	      /* pointer to tty structure if present */  	struct raw3215_req *queued_read; /* pointer to queued read requests */  	struct raw3215_req *queued_write;/* pointer to queued write requests */ +	struct tasklet_struct tlet;   /* tasklet to invoke tty_wakeup */  	wait_queue_head_t empty_wait; /* wait queue for flushing */  	struct timer_list timer;      /* timer for delayed output */  	int line_pos;		      /* position on the line (for tabs) */ @@ -292,7 +291,7 @@ static void raw3215_timeout(unsigned long __data)  	if (raw->flags & RAW3215_TIMER_RUNS) {  		del_timer(&raw->timer);  		raw->flags &= ~RAW3215_TIMER_RUNS; -		if (!(raw->flags & RAW3215_FROZEN)) { +		if (!(raw->port.flags & ASYNC_SUSPENDED)) {  			raw3215_mk_write_req(raw);  			raw3215_start_io(raw);  		} @@ -308,7 +307,8 @@ static void raw3215_timeout(unsigned long __data)   */  static inline void raw3215_try_io(struct raw3215_info *raw)  { -	if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FROZEN)) +	if (!(raw->port.flags & ASYNC_INITIALIZED) || +			(raw->port.flags & ASYNC_SUSPENDED))  		return;  	if (raw->queued_read != NULL)  		raw3215_start_io(raw); @@ -323,10 +323,7 @@ static inline void raw3215_try_io(struct raw3215_info *raw)  			}  		} else if (!(raw->flags & RAW3215_TIMER_RUNS)) {  			/* delay small writes */ -			init_timer(&raw->timer);  			raw->timer.expires = RAW3215_TIMEOUT + jiffies; -			raw->timer.data = (unsigned long) raw; -			raw->timer.function = raw3215_timeout;  			add_timer(&raw->timer);  			raw->flags |= RAW3215_TIMER_RUNS;  		} @@ -334,19 +331,29 @@ static inline void raw3215_try_io(struct raw3215_info *raw)  }  /* - * Try to start the next IO and wake up processes waiting on the tty. + * Call tty_wakeup from tasklet context   */ -static void raw3215_next_io(struct raw3215_info *raw) +static void raw3215_wakeup(unsigned long data)  { +	struct raw3215_info *raw = (struct raw3215_info *) data;  	struct tty_struct *tty; +	tty = tty_port_tty_get(&raw->port); +	if (tty) { +		tty_wakeup(tty); +		tty_kref_put(tty); +	} +} + +/* + * Try to start the next IO and wake up processes waiting on the tty. + */ +static void raw3215_next_io(struct raw3215_info *raw, struct tty_struct *tty) +{  	raw3215_mk_write_req(raw);  	raw3215_try_io(raw); -	tty = raw->tty; -	if (tty != NULL && -	    RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) { -	    	tty_wakeup(tty); -	} +	if (tty && RAW3215_BUFFER_SIZE - raw->count >= RAW3215_MIN_SPACE) +		tasklet_schedule(&raw->tlet);  }  /* @@ -363,10 +370,11 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  	raw = dev_get_drvdata(&cdev->dev);  	req = (struct raw3215_req *) intparm; +	tty = tty_port_tty_get(&raw->port);  	cstat = irb->scsw.cmd.cstat;  	dstat = irb->scsw.cmd.dstat;  	if (cstat != 0) -		raw3215_next_io(raw); +		raw3215_next_io(raw, tty);  	if (dstat & 0x01) { /* we got a unit exception */  		dstat &= ~0x01;	 /* we can ignore it */  	} @@ -376,13 +384,13 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  			break;  		/* Attention interrupt, someone hit the enter key */  		raw3215_mk_read_req(raw); -		raw3215_next_io(raw); +		raw3215_next_io(raw, tty);  		break;  	case 0x08:  	case 0x0C:  		/* Channel end interrupt. */  		if ((raw = req->info) == NULL) -			return;		     /* That shouldn't happen ... */ +			goto put_tty;	     /* That shouldn't happen ... */  		if (req->type == RAW3215_READ) {  			/* store residual count, then wait for device end */  			req->residual = irb->scsw.cmd.count; @@ -392,11 +400,10 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  	case 0x04:  		/* Device end interrupt. */  		if ((raw = req->info) == NULL) -			return;		     /* That shouldn't happen ... */ -		if (req->type == RAW3215_READ && raw->tty != NULL) { +			goto put_tty;	     /* That shouldn't happen ... */ +		if (req->type == RAW3215_READ && tty != NULL) {  			unsigned int cchar; -			tty = raw->tty;  			count = 160 - req->residual;  			EBCASC(raw->inbuf, count);  			cchar = ctrlchar_handle(raw->inbuf, count, tty); @@ -405,8 +412,9 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  				break;  			case CTRLCHAR_CTRL: -				tty_insert_flip_char(tty, cchar, TTY_NORMAL); -				tty_flip_buffer_push(raw->tty); +				tty_insert_flip_char(&raw->port, cchar, +						TTY_NORMAL); +				tty_flip_buffer_push(&raw->port);  				break;  			case CTRLCHAR_NONE: @@ -418,8 +426,9 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  					count++;  				} else  					count -= 2; -				tty_insert_flip_string(tty, raw->inbuf, count); -				tty_flip_buffer_push(raw->tty); +				tty_insert_flip_string(&raw->port, raw->inbuf, +						count); +				tty_flip_buffer_push(&raw->port);  				break;  			}  		} else if (req->type == RAW3215_WRITE) { @@ -434,7 +443,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  		    raw->queued_read == NULL) {  			wake_up_interruptible(&raw->empty_wait);  		} -		raw3215_next_io(raw); +		raw3215_next_io(raw, tty);  		break;  	default:  		/* Strange interrupt, I'll do my best to clean up */ @@ -446,9 +455,10 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm,  			raw->flags &= ~RAW3215_WORKING;  			raw3215_free_req(req);  		} -		raw3215_next_io(raw); +		raw3215_next_io(raw, tty);  	} -	return; +put_tty: +	tty_kref_put(tty);  }  /* @@ -482,7 +492,7 @@ static void raw3215_make_room(struct raw3215_info *raw, unsigned int length)  		/* While console is frozen for suspend we have no other  		 * choice but to drop message from the buffer to make  		 * room for even more messages. */ -		if (raw->flags & RAW3215_FROZEN) { +		if (raw->port.flags & ASYNC_SUSPENDED) {  			raw3215_drop_line(raw);  			continue;  		} @@ -492,7 +502,7 @@ static void raw3215_make_room(struct raw3215_info *raw, unsigned int length)  		raw3215_try_io(raw);  		raw->flags &= ~RAW3215_FLUSHING;  #ifdef CONFIG_TN3215_CONSOLE -		wait_cons_dev(); +		ccw_device_wait_idle(raw->cdev);  #endif  		/* Enough room freed up ? */  		if (RAW3215_BUFFER_SIZE - raw->count >= length) @@ -604,10 +614,10 @@ static int raw3215_startup(struct raw3215_info *raw)  {  	unsigned long flags; -	if (raw->flags & RAW3215_ACTIVE) +	if (raw->port.flags & ASYNC_INITIALIZED)  		return 0;  	raw->line_pos = 0; -	raw->flags |= RAW3215_ACTIVE; +	raw->port.flags |= ASYNC_INITIALIZED;  	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);  	raw3215_try_io(raw);  	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -623,14 +633,15 @@ static void raw3215_shutdown(struct raw3215_info *raw)  	DECLARE_WAITQUEUE(wait, current);  	unsigned long flags; -	if (!(raw->flags & RAW3215_ACTIVE) || (raw->flags & RAW3215_FIXED)) +	if (!(raw->port.flags & ASYNC_INITIALIZED) || +	    (raw->flags & RAW3215_FIXED))  		return;  	/* Wait for outstanding requests, then free irq */  	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);  	if ((raw->flags & RAW3215_WORKING) ||  	    raw->queued_write != NULL ||  	    raw->queued_read != NULL) { -		raw->flags |= RAW3215_CLOSING; +		raw->port.flags |= ASYNC_CLOSING;  		add_wait_queue(&raw->empty_wait, &wait);  		set_current_state(TASK_INTERRUPTIBLE);  		spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); @@ -638,11 +649,42 @@ static void raw3215_shutdown(struct raw3215_info *raw)  		spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);  		remove_wait_queue(&raw->empty_wait, &wait);  		set_current_state(TASK_RUNNING); -		raw->flags &= ~(RAW3215_ACTIVE | RAW3215_CLOSING); +		raw->port.flags &= ~(ASYNC_INITIALIZED | ASYNC_CLOSING);  	}  	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);  } +static struct raw3215_info *raw3215_alloc_info(void) +{ +	struct raw3215_info *info; + +	info = kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA); +	if (!info) +		return NULL; + +	info->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); +	info->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA); +	if (!info->buffer || !info->inbuf) { +		kfree(info); +		return NULL; +	} + +	setup_timer(&info->timer, raw3215_timeout, (unsigned long)info); +	init_waitqueue_head(&info->empty_wait); +	tasklet_init(&info->tlet, raw3215_wakeup, (unsigned long)info); +	tty_port_init(&info->port); + +	return info; +} + +static void raw3215_free_info(struct raw3215_info *raw) +{ +	kfree(raw->inbuf); +	kfree(raw->buffer); +	tty_port_destroy(&raw->port); +	kfree(raw); +} +  static int raw3215_probe (struct ccw_device *cdev)  {  	struct raw3215_info *raw; @@ -651,11 +693,15 @@ static int raw3215_probe (struct ccw_device *cdev)  	/* Console is special. */  	if (raw3215[0] && (raw3215[0] == dev_get_drvdata(&cdev->dev)))  		return 0; -	raw = kmalloc(sizeof(struct raw3215_info) + -		      RAW3215_INBUF_SIZE, GFP_KERNEL|GFP_DMA); + +	raw = raw3215_alloc_info();  	if (raw == NULL)  		return -ENOMEM; +	raw->cdev = cdev; +	dev_set_drvdata(&cdev->dev, raw); +	cdev->handler = raw3215_irq; +  	spin_lock(&raw3215_device_lock);  	for (line = 0; line < NR_3215; line++) {  		if (!raw3215[line]) { @@ -665,40 +711,29 @@ static int raw3215_probe (struct ccw_device *cdev)  	}  	spin_unlock(&raw3215_device_lock);  	if (line == NR_3215) { -		kfree(raw); +		raw3215_free_info(raw);  		return -ENODEV;  	} -	raw->cdev = cdev; -	raw->inbuf = (char *) raw + sizeof(struct raw3215_info); -	memset(raw, 0, sizeof(struct raw3215_info)); -	raw->buffer = kmalloc(RAW3215_BUFFER_SIZE, -				       GFP_KERNEL|GFP_DMA); -	if (raw->buffer == NULL) { -		spin_lock(&raw3215_device_lock); -		raw3215[line] = NULL; -		spin_unlock(&raw3215_device_lock); -		kfree(raw); -		return -ENOMEM; -	} -	init_waitqueue_head(&raw->empty_wait); - -	dev_set_drvdata(&cdev->dev, raw); -	cdev->handler = raw3215_irq; -  	return 0;  }  static void raw3215_remove (struct ccw_device *cdev)  {  	struct raw3215_info *raw; +	unsigned int line;  	ccw_device_set_offline(cdev);  	raw = dev_get_drvdata(&cdev->dev);  	if (raw) { +		spin_lock(&raw3215_device_lock); +		for (line = 0; line < NR_3215; line++) +			if (raw3215[line] == raw) +				break; +		raw3215[line] = NULL; +		spin_unlock(&raw3215_device_lock);  		dev_set_drvdata(&cdev->dev, NULL); -		kfree(raw->buffer); -		kfree(raw); +		raw3215_free_info(raw);  	}  } @@ -735,7 +770,7 @@ static int raw3215_pm_stop(struct ccw_device *cdev)  	raw = dev_get_drvdata(&cdev->dev);  	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags);  	raw3215_make_room(raw, RAW3215_BUFFER_SIZE); -	raw->flags |= RAW3215_FROZEN; +	raw->port.flags |= ASYNC_SUSPENDED;  	spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags);  	return 0;  } @@ -748,7 +783,7 @@ static int raw3215_pm_start(struct ccw_device *cdev)  	/* Allow I/O again and flush output buffer. */  	raw = dev_get_drvdata(&cdev->dev);  	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); -	raw->flags &= ~RAW3215_FROZEN; +	raw->port.flags &= ~ASYNC_SUSPENDED;  	raw->flags |= RAW3215_FLUSHING;  	raw3215_try_io(raw);  	raw->flags &= ~RAW3215_FLUSHING; @@ -762,8 +797,10 @@ static struct ccw_device_id raw3215_id[] = {  };  static struct ccw_driver raw3215_ccw_driver = { -	.name		= "3215", -	.owner		= THIS_MODULE, +	.driver = { +		.name	= "3215", +		.owner	= THIS_MODULE, +	},  	.ids		= raw3215_id,  	.probe		= &raw3215_probe,  	.remove		= &raw3215_remove, @@ -772,6 +809,7 @@ static struct ccw_driver raw3215_ccw_driver = {  	.freeze		= &raw3215_pm_stop,  	.thaw		= &raw3215_pm_start,  	.restore	= &raw3215_pm_start, +	.int_class	= IRQIO_C15,  };  #ifdef CONFIG_TN3215_CONSOLE @@ -818,9 +856,9 @@ static void con3215_flush(void)  	unsigned long flags;  	raw = raw3215[0];  /* console 3215 is the first one */ -	if (raw->flags & RAW3215_FROZEN) +	if (raw->port.flags & ASYNC_SUSPENDED)  		/* The console is still frozen for suspend. */ -		if (ccw_device_force_console()) +		if (ccw_device_force_console(raw->cdev))  			/* Forcing didn't work, no panic message .. */  			return;  	spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); @@ -884,26 +922,26 @@ static int __init con3215_init(void)  		raw3215_freelist = req;  	} -	cdev = ccw_device_probe_console(); +	cdev = ccw_device_create_console(&raw3215_ccw_driver);  	if (IS_ERR(cdev))  		return -ENODEV; -	raw3215[0] = raw = (struct raw3215_info *) -		kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA); -	raw->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); -	raw->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA); +	raw3215[0] = raw = raw3215_alloc_info();  	raw->cdev = cdev;  	dev_set_drvdata(&cdev->dev, raw);  	cdev->handler = raw3215_irq;  	raw->flags |= RAW3215_FIXED; -	init_waitqueue_head(&raw->empty_wait); +	if (ccw_device_enable_console(cdev)) { +		ccw_device_destroy_console(cdev); +		raw3215_free_info(raw); +		raw3215[0] = NULL; +		return -ENODEV; +	}  	/* Request the console irq */  	if (raw3215_startup(raw) != 0) { -		kfree(raw->inbuf); -		kfree(raw->buffer); -		kfree(raw); +		raw3215_free_info(raw);  		raw3215[0] = NULL;  		return -ENODEV;  	} @@ -915,6 +953,19 @@ static int __init con3215_init(void)  console_initcall(con3215_init);  #endif +static int tty3215_install(struct tty_driver *driver, struct tty_struct *tty) +{ +	struct raw3215_info *raw; + +	raw = raw3215[tty->index]; +	if (raw == NULL) +		return -ENODEV; + +	tty->driver_data = raw; + +	return tty_port_install(&raw->port, driver, tty); +} +  /*   * tty3215_open   * @@ -922,21 +973,12 @@ console_initcall(con3215_init);   */  static int tty3215_open(struct tty_struct *tty, struct file * filp)  { -	struct raw3215_info *raw; -	int retval, line; - -	line = tty->index; -	if ((line < 0) || (line >= NR_3215)) -		return -ENODEV; +	struct raw3215_info *raw = tty->driver_data; +	int retval; -	raw = raw3215[line]; -	if (raw == NULL) -		return -ENODEV; - -	tty->driver_data = raw; -	raw->tty = tty; +	tty_port_tty_set(&raw->port, tty); -	tty->low_latency = 0;  /* don't use bottom half for pushing chars */ +	raw->port.low_latency = 0; /* don't use bottom half for pushing chars */  	/*  	 * Start up 3215 device  	 */ @@ -963,8 +1005,9 @@ static void tty3215_close(struct tty_struct *tty, struct file * filp)  	tty->closing = 1;  	/* Shutdown the terminal */  	raw3215_shutdown(raw); +	tasklet_kill(&raw->tlet);  	tty->closing = 0; -	raw->tty = NULL; +	tty_port_tty_set(&raw->port, NULL);  }  /* @@ -1093,6 +1136,7 @@ static void tty3215_start(struct tty_struct *tty)  }  static const struct tty_operations tty3215_ops = { +	.install = tty3215_install,  	.open = tty3215_open,  	.close = tty3215_close,  	.write = tty3215_write, @@ -1134,7 +1178,6 @@ static int __init tty3215_init(void)  	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc  	 */ -	driver->owner = THIS_MODULE;  	driver->driver_name = "tty3215";  	driver->name = "ttyS";  	driver->major = TTY_MAJOR; diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index bb07577e8fd..75ffe9980c3 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -7,6 +7,7 @@   *     Copyright IBM Corp. 2003, 2009   */ +#include <linux/module.h>  #include <linux/console.h>  #include <linux/init.h>  #include <linux/interrupt.h> @@ -30,12 +31,14 @@  static struct raw3270_fn con3270_fn; +static bool auto_update = 1; +module_param(auto_update, bool, 0); +  /*   * Main 3270 console view data structure.   */  struct con3270 {  	struct raw3270_view view; -	spinlock_t lock;  	struct list_head freemem;	/* list of free memory for strings. */  	/* Output stuff. */ @@ -205,6 +208,8 @@ con3270_update(struct con3270 *cp)  	struct string *s, *n;  	int rc; +	if (!auto_update && !raw3270_view_active(&cp->view)) +		return;  	if (cp->view.dev)  		raw3270_activate_view(&cp->view); @@ -530,6 +535,7 @@ con3270_flush(void)  	if (!cp->view.dev)  		return;  	raw3270_pm_unfreeze(&cp->view); +	raw3270_activate_view(&cp->view);  	spin_lock_irqsave(&cp->view.lock, flags);  	con3270_wait_write(cp);  	cp->nr_up = 0; @@ -577,7 +583,6 @@ static struct console con3270 = {  static int __init  con3270_init(void)  { -	struct ccw_device *cdev;  	struct raw3270 *rp;  	void *cbuf;  	int i; @@ -592,10 +597,7 @@ con3270_init(void)  		cpcmd("TERM AUTOCR OFF", NULL, 0, NULL);  	} -	cdev = ccw_device_probe_console(); -	if (IS_ERR(cdev)) -		return -ENODEV; -	rp = raw3270_setup_console(cdev); +	rp = raw3270_setup_console();  	if (IS_ERR(rp))  		return PTR_ERR(rp); diff --git a/drivers/s390/char/ctrlchar.c b/drivers/s390/char/ctrlchar.c index 0e9a309b966..8de2deb176d 100644 --- a/drivers/s390/char/ctrlchar.c +++ b/drivers/s390/char/ctrlchar.c @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/ctrlchar.c   *  Unified handling of special chars.   * - *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 2001   *    Author(s): Fritz Elfert <felfert@millenux.com> <elfert@de.ibm.com>   *   */ diff --git a/drivers/s390/char/ctrlchar.h b/drivers/s390/char/ctrlchar.h index 935ffa0ea7c..1a53552f498 100644 --- a/drivers/s390/char/ctrlchar.h +++ b/drivers/s390/char/ctrlchar.h @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/ctrlchar.c   *  Unified handling of special chars.   * - *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 2001   *    Author(s): Fritz Elfert <felfert@millenux.com> <elfert@de.ibm.com>   *   */ diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index eb28fb01a38..71e97473801 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -11,10 +11,11 @@  #include <linux/console.h>  #include <linux/init.h>  #include <linux/interrupt.h> +#include <linux/compat.h> +#include <linux/module.h>  #include <linux/list.h>  #include <linux/slab.h>  #include <linux/types.h> -#include <linux/smp_lock.h>  #include <asm/compat.h>  #include <asm/ccwdev.h> @@ -432,9 +433,9 @@ fs3270_open(struct inode *inode, struct file *filp)  	struct idal_buffer *ib;  	int minor, rc = 0; -	if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR) +	if (imajor(file_inode(filp)) != IBM_FS3270_MAJOR)  		return -ENODEV; -	minor = iminor(filp->f_path.dentry->d_inode); +	minor = iminor(file_inode(filp));  	/* Check for minor 0 multiplexer. */  	if (minor == 0) {  		struct tty_struct *tty = get_current_tty(); @@ -442,7 +443,7 @@ fs3270_open(struct inode *inode, struct file *filp)  			tty_kref_put(tty);  			return -ENODEV;  		} -		minor = tty->index + RAW3270_FIRSTMINOR; +		minor = tty->index;  		tty_kref_put(tty);  	}  	mutex_lock(&fs3270_mutex); @@ -523,6 +524,25 @@ static const struct file_operations fs3270_fops = {  	.llseek		= no_llseek,  }; +static void fs3270_create_cb(int minor) +{ +	__register_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub", &fs3270_fops); +	device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), +		      NULL, "3270/tub%d", minor); +} + +static void fs3270_destroy_cb(int minor) +{ +	device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, minor)); +	__unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); +} + +static struct raw3270_notifier fs3270_notifier = +{ +	.create = fs3270_create_cb, +	.destroy = fs3270_destroy_cb, +}; +  /*   * 3270 fullscreen driver initialization.   */ @@ -531,16 +551,21 @@ fs3270_init(void)  {  	int rc; -	rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); +	rc = __register_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270", &fs3270_fops);  	if (rc)  		return rc; +	device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), +		      NULL, "3270/tub"); +	raw3270_register_notifier(&fs3270_notifier);  	return 0;  }  static void __exit  fs3270_exit(void)  { -	unregister_chrdev(IBM_FS3270_MAJOR, "fs3270"); +	raw3270_unregister_notifier(&fs3270_notifier); +	device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, 0)); +	__unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270");  }  MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index 8cd58e412b5..01463b052ae 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/keyboard.c   *    ebcdic keycode functions for s390 console drivers   *   *  S390 version - *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 2003   *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),   */ @@ -199,7 +198,7 @@ handle_diacr(struct kbd_data *kbd, unsigned int ch)  	if (ch == ' ' || ch == d)  		return d; -	kbd_put_queue(kbd->tty, d); +	kbd_put_queue(kbd->port, d);  	return ch;  } @@ -221,7 +220,7 @@ k_self(struct kbd_data *kbd, unsigned char value)  {  	if (kbd->diacr)  		value = handle_diacr(kbd, value); -	kbd_put_queue(kbd->tty, value); +	kbd_put_queue(kbd->port, value);  }  /* @@ -239,7 +238,7 @@ static void  k_fn(struct kbd_data *kbd, unsigned char value)  {  	if (kbd->func_table[value]) -		kbd_puts_queue(kbd->tty, kbd->func_table[value]); +		kbd_puts_queue(kbd->port, kbd->func_table[value]);  }  static void @@ -257,20 +256,20 @@ k_spec(struct kbd_data *kbd, unsigned char value)   * but we need only 16 bits here   */  static void -to_utf8(struct tty_struct *tty, ushort c)  +to_utf8(struct tty_port *port, ushort c)  {  	if (c < 0x80)  		/*  0******* */ -		kbd_put_queue(tty, c); +		kbd_put_queue(port, c);  	else if (c < 0x800) {  		/* 110***** 10****** */ -		kbd_put_queue(tty, 0xc0 | (c >> 6)); -		kbd_put_queue(tty, 0x80 | (c & 0x3f)); +		kbd_put_queue(port, 0xc0 | (c >> 6)); +		kbd_put_queue(port, 0x80 | (c & 0x3f));  	} else {  		/* 1110**** 10****** 10****** */ -		kbd_put_queue(tty, 0xe0 | (c >> 12)); -		kbd_put_queue(tty, 0x80 | ((c >> 6) & 0x3f)); -		kbd_put_queue(tty, 0x80 | (c & 0x3f)); +		kbd_put_queue(port, 0xe0 | (c >> 12)); +		kbd_put_queue(port, 0x80 | ((c >> 6) & 0x3f)); +		kbd_put_queue(port, 0x80 | (c & 0x3f));  	}  } @@ -283,7 +282,7 @@ kbd_keycode(struct kbd_data *kbd, unsigned int keycode)  	unsigned short keysym;  	unsigned char type, value; -	if (!kbd || !kbd->tty) +	if (!kbd)  		return;  	if (keycode >= 384) @@ -323,7 +322,7 @@ kbd_keycode(struct kbd_data *kbd, unsigned int keycode)  #endif  		(*k_handler[type])(kbd, value);  	} else -		to_utf8(kbd->tty, keysym); +		to_utf8(kbd->port, keysym);  }  /* @@ -455,12 +454,12 @@ do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,  	return 0;  } -int -kbd_ioctl(struct kbd_data *kbd, struct file *file, -	  unsigned int cmd, unsigned long arg) +int kbd_ioctl(struct kbd_data *kbd, unsigned int cmd, unsigned long arg)  { +	struct tty_struct *tty;  	void __user *argp; -	int ct, perm; +	unsigned int ct; +	int perm;  	argp = (void __user *)arg; @@ -468,7 +467,10 @@ kbd_ioctl(struct kbd_data *kbd, struct file *file,  	 * To have permissions to do most of the vt ioctls, we either have  	 * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG.  	 */ -	perm = current->signal->tty == kbd->tty || capable(CAP_SYS_TTY_CONFIG); +	tty = tty_port_tty_get(kbd->port); +	/* FIXME this test is pretty racy */ +	perm = current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG); +	tty_kref_put(tty);  	switch (cmd) {  	case KDGKBTYPE:  		return put_user(KB_101, (char __user *)argp); diff --git a/drivers/s390/char/keyboard.h b/drivers/s390/char/keyboard.h index 5ccfe9cf126..a31f339211d 100644 --- a/drivers/s390/char/keyboard.h +++ b/drivers/s390/char/keyboard.h @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/keyboard.h   *    ebcdic keycode functions for s390 console drivers   * - *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 2003   *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),   */ @@ -21,7 +20,7 @@ typedef void (fn_handler_fn)(struct kbd_data *);   */  struct kbd_data { -	struct tty_struct *tty; +	struct tty_port *port;  	unsigned short **key_maps;  	char **func_table;  	fn_handler_fn **fn_handler; @@ -36,22 +35,22 @@ void kbd_free(struct kbd_data *);  void kbd_ascebc(struct kbd_data *, unsigned char *);  void kbd_keycode(struct kbd_data *, unsigned int); -int kbd_ioctl(struct kbd_data *, struct file *, unsigned int, unsigned long); +int kbd_ioctl(struct kbd_data *, unsigned int, unsigned long);  /*   * Helper Functions.   */  static inline void -kbd_put_queue(struct tty_struct *tty, int ch) +kbd_put_queue(struct tty_port *port, int ch)  { -	tty_insert_flip_char(tty, ch, 0); -	tty_schedule_flip(tty); +	tty_insert_flip_char(port, ch, 0); +	tty_schedule_flip(port);  }  static inline void -kbd_puts_queue(struct tty_struct *tty, char *cp) +kbd_puts_queue(struct tty_port *port, char *cp)  {  	while (*cp) -		tty_insert_flip_char(tty, *cp++, 0); -	tty_schedule_flip(tty); +		tty_insert_flip_char(port, *cp++, 0); +	tty_schedule_flip(port);  } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 5b8b8592d31..0da3ae3cd63 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -174,8 +174,7 @@ static void mon_free_mem(struct mon_private *monpriv)  	int i;  	for (i = 0; i < MON_MSGLIM; i++) -		if (monpriv->msg_array[i]) -			kfree(monpriv->msg_array[i]); +		kfree(monpriv->msg_array[i]);  	kfree(monpriv);  } @@ -571,8 +570,11 @@ static int __init mon_init(void)  	if (rc)  		goto out_iucv;  	monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL); -	if (!monreader_device) +	if (!monreader_device) { +		rc = -ENOMEM;  		goto out_driver; +	} +  	dev_set_name(monreader_device, "monreader-dev");  	monreader_device->bus = &iucv_bus;  	monreader_device->parent = iucv_root; diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index e0702d3ea33..668b32b0dc1 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -60,7 +60,7 @@ static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)  	struct appldata_product_id id;  	int rc; -	strcpy(id.prod_nr, "LNXAPPL"); +	strncpy(id.prod_nr, "LNXAPPL", 7);  	id.prod_fn = myhdr->applid;  	id.record_nr = myhdr->record_num;  	id.version_nr = myhdr->version; @@ -97,7 +97,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)  {  	struct monwrite_hdr *monhdr = &monpriv->hdr;  	struct mon_buf *monbuf; -	int rc; +	int rc = 0;  	if (monhdr->datalen > MONWRITE_MAX_DATALEN ||  	    monhdr->mon_function > MONWRITE_START_CONFIG || @@ -135,7 +135,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)  			mon_buf_count++;  	}  	monpriv->current_buf = monbuf; -	return 0; +	return rc;  }  static int monwrite_new_data(struct mon_private *monpriv) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 2a4c566456e..220acb4cbee 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -28,7 +28,7 @@  #include <linux/device.h>  #include <linux/mutex.h> -static struct class *class3270; +struct class *class3270;  /* The main 3270 data structure. */  struct raw3270 { @@ -37,6 +37,7 @@ struct raw3270 {  	int minor;  	short model, rows, cols; +	unsigned int state;  	unsigned long flags;  	struct list_head req_queue;	/* Request queue. */ @@ -46,20 +47,26 @@ struct raw3270 {  	struct timer_list timer;	/* Device timer. */  	unsigned char *ascebc;		/* ascii -> ebcdic table */ -	struct device *clttydev;	/* 3270-class tty device ptr */ -	struct device *cltubdev;	/* 3270-class tub device ptr */ -	struct raw3270_request init_request; +	struct raw3270_view init_view; +	struct raw3270_request init_reset; +	struct raw3270_request init_readpart; +	struct raw3270_request init_readmod;  	unsigned char init_data[256];  }; +/* raw3270->state */ +#define RAW3270_STATE_INIT	0	/* Initial state */ +#define RAW3270_STATE_RESET	1	/* Reset command is pending */ +#define RAW3270_STATE_W4ATTN	2	/* Wait for attention interrupt */ +#define RAW3270_STATE_READMOD	3	/* Read partition is pending */ +#define RAW3270_STATE_READY	4	/* Device is usable by views */ +  /* raw3270->flags */  #define RAW3270_FLAGS_14BITADDR	0	/* 14-bit buffer addresses */  #define RAW3270_FLAGS_BUSY	1	/* Device busy, leave it alone */ -#define RAW3270_FLAGS_ATTN	2	/* Device sent an ATTN interrupt */ -#define RAW3270_FLAGS_READY	4	/* Device is useable by views */ -#define RAW3270_FLAGS_CONSOLE	8	/* Device is the console. */ -#define RAW3270_FLAGS_FROZEN	16	/* set if 3270 is frozen for suspend */ +#define RAW3270_FLAGS_CONSOLE	2	/* Device is the console. */ +#define RAW3270_FLAGS_FROZEN	3	/* set if 3270 is frozen for suspend */  /* Semaphore to protect global data of raw3270 (devices, views, etc). */  static DEFINE_MUTEX(raw3270_mutex); @@ -75,7 +82,7 @@ static LIST_HEAD(raw3270_devices);  static int raw3270_registered;  /* Module parameters */ -static int tubxcorrect = 0; +static bool tubxcorrect = 0;  module_param(tubxcorrect, bool, 0);  /* @@ -97,6 +104,17 @@ static unsigned char raw3270_ebcgraf[64] =	{  	0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f  }; +static inline int raw3270_state_ready(struct raw3270 *rp) +{ +	return rp->state == RAW3270_STATE_READY; +} + +static inline int raw3270_state_final(struct raw3270 *rp) +{ +	return rp->state == RAW3270_STATE_INIT || +		rp->state == RAW3270_STATE_READY; +} +  void  raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr)  { @@ -214,7 +232,7 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib)   * Stop running ccw.   */  static int -raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) +__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq)  {  	int retries;  	int rc; @@ -233,18 +251,6 @@ raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq)  	return rc;  } -static int -raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) -{ -	unsigned long flags; -	int rc; - -	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -	rc = raw3270_halt_io_nolock(rp, rq); -	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); -	return rc; -} -  /*   * Add the request to the request queue, try to start it if the   * 3270 device is idle. Return without waiting for end of i/o. @@ -270,6 +276,15 @@ __raw3270_start(struct raw3270 *rp, struct raw3270_view *view,  }  int +raw3270_view_active(struct raw3270_view *view) +{ +	struct raw3270 *rp = view->dev; + +	return rp && rp->view == view && +		!test_bit(RAW3270_FLAGS_FROZEN, &rp->flags); +} + +int  raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)  {  	unsigned long flags; @@ -281,8 +296,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq)  	if (!rp || rp->view != view ||  	    test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))  		rc = -EACCES; -	else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) -		rc = -ENODEV; +	else if (!raw3270_state_ready(rp)) +		rc = -EBUSY;  	else  		rc =  __raw3270_start(rp, view, rq);  	spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); @@ -299,8 +314,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq)  	if (!rp || rp->view != view ||  	    test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))  		rc = -EACCES; -	else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) -		rc = -ENODEV; +	else if (!raw3270_state_ready(rp)) +		rc = -EBUSY;  	else  		rc =  __raw3270_start(rp, view, rq);  	return rc; @@ -378,7 +393,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)  	case RAW3270_IO_STOP:  		if (!rq)  			break; -		raw3270_halt_io_nolock(rp, rq); +		__raw3270_halt_io(rp, rq);  		rq->rc = -EIO;  		break;  	default: @@ -413,9 +428,14 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)  }  /* - * Size sensing. + * To determine the size of the 3270 device we need to do: + * 1) send a 'read partition' data stream to the device + * 2) wait for the attn interrupt that precedes the query reply + * 3) do a read modified to get the query reply + * To make things worse we have to cope with intervention + * required (3270 device switched to 'stand-by') and command + * rejects (old devices that can't do 'read partition').   */ -  struct raw3270_ua {	/* Query Reply structure for Usable Area */  	struct {	/* Usable Area Query Reply Base */  		short l;	/* Length of this structured field */ @@ -451,117 +471,21 @@ struct raw3270_ua {	/* Query Reply structure for Usable Area */  	} __attribute__ ((packed)) aua;  } __attribute__ ((packed)); -static struct diag210 raw3270_init_diag210; -static DEFINE_MUTEX(raw3270_init_mutex); - -static int -raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, -		 struct irb *irb) -{ -	/* -	 * Unit-Check Processing: -	 * Expect Command Reject or Intervention Required. -	 */ -	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { -		/* Request finished abnormally. */ -		if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { -			set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); -			return RAW3270_IO_BUSY; -		} -	} -	if (rq) { -		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { -			if (irb->ecw[0] & SNS0_CMD_REJECT) -				rq->rc = -EOPNOTSUPP; -			else -				rq->rc = -EIO; -		} else -			/* Request finished normally. Copy residual count. */ -			rq->rescnt = irb->scsw.cmd.count; -	} -	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { -		set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); -		wake_up(&raw3270_wait_queue); -	} -	return RAW3270_IO_DONE; -} - -static struct raw3270_fn raw3270_init_fn = { -	.intv = raw3270_init_irq -}; - -static struct raw3270_view raw3270_init_view = { -	.fn = &raw3270_init_fn -}; - -/* - * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup - * Wait for end of request. The request must have been started - * with raw3270_start, rc = 0. The device lock may NOT have been - * released between calling raw3270_start and raw3270_wait. - */  static void -raw3270_wake_init(struct raw3270_request *rq, void *data) -{ -	wake_up((wait_queue_head_t *) data); -} - -/* - * Special wait function that can cope with console initialization. - */ -static int -raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view, -		   struct raw3270_request *rq) -{ -	unsigned long flags; -	int rc; - -#ifdef CONFIG_TN3270_CONSOLE -	if (raw3270_registered == 0) { -		spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); -		rq->callback = NULL; -		rc = __raw3270_start(rp, view, rq); -		if (rc == 0) -			while (!raw3270_request_final(rq)) { -				wait_cons_dev(); -				barrier(); -			} -		spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); -		return rq->rc; -	} -#endif -	rq->callback = raw3270_wake_init; -	rq->callback_data = &raw3270_wait_queue; -	spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); -	rc = __raw3270_start(rp, view, rq); -	spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); -	if (rc) -		return rc; -	/* Now wait for the completion. */ -	rc = wait_event_interruptible(raw3270_wait_queue, -				      raw3270_request_final(rq)); -	if (rc == -ERESTARTSYS) {	/* Interrupted by a signal. */ -		raw3270_halt_io(view->dev, rq); -		/* No wait for the halt to complete. */ -		wait_event(raw3270_wait_queue, raw3270_request_final(rq)); -		return -ERESTARTSYS; -	} -	return rq->rc; -} - -static int -__raw3270_size_device_vm(struct raw3270 *rp) +raw3270_size_device_vm(struct raw3270 *rp)  {  	int rc, model;  	struct ccw_dev_id dev_id; +	struct diag210 diag_data;  	ccw_device_get_id(rp->cdev, &dev_id); -	raw3270_init_diag210.vrdcdvno = dev_id.devno; -	raw3270_init_diag210.vrdclen = sizeof(struct diag210); -	rc = diag210(&raw3270_init_diag210); -	if (rc) -		return rc; -	model = raw3270_init_diag210.vrdccrmd; +	diag_data.vrdcdvno = dev_id.devno; +	diag_data.vrdclen = sizeof(struct diag210); +	rc = diag210(&diag_data); +	model = diag_data.vrdccrmd; +	/* Use default model 2 if the size could not be detected */ +	if (rc || model < 2 || model > 5) +		model = 2;  	switch (model) {  	case 2:  		rp->model = model; @@ -583,79 +507,25 @@ __raw3270_size_device_vm(struct raw3270 *rp)  		rp->rows = 27;  		rp->cols = 132;  		break; -	default: -		rc = -EOPNOTSUPP; -		break;  	} -	return rc;  } -static int -__raw3270_size_device(struct raw3270 *rp) +static void +raw3270_size_device(struct raw3270 *rp)  { -	static const unsigned char wbuf[] = -		{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };  	struct raw3270_ua *uap; -	unsigned short count; -	int rc; - -	/* -	 * To determine the size of the 3270 device we need to do: -	 * 1) send a 'read partition' data stream to the device -	 * 2) wait for the attn interrupt that preceeds the query reply -	 * 3) do a read modified to get the query reply -	 * To make things worse we have to cope with intervention -	 * required (3270 device switched to 'stand-by') and command -	 * rejects (old devices that can't do 'read partition'). -	 */ -	memset(&rp->init_request, 0, sizeof(rp->init_request)); -	memset(&rp->init_data, 0, 256); -	/* Store 'read partition' data stream to init_data */ -	memcpy(&rp->init_data, wbuf, sizeof(wbuf)); -	INIT_LIST_HEAD(&rp->init_request.list); -	rp->init_request.ccw.cmd_code = TC_WRITESF; -	rp->init_request.ccw.flags = CCW_FLAG_SLI; -	rp->init_request.ccw.count = sizeof(wbuf); -	rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data); - -	rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); -	if (rc) -		/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ -		return rc; -	/* Wait for attention interrupt. */ -#ifdef CONFIG_TN3270_CONSOLE -	if (raw3270_registered == 0) { -		unsigned long flags; - -		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -		while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)) -			wait_cons_dev(); -		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); -	} else -#endif -		rc = wait_event_interruptible(raw3270_wait_queue, -			test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)); -	if (rc) -		return rc; - -	/* -	 * The device accepted the 'read partition' command. Now -	 * set up a read ccw and issue it. -	 */ -	rp->init_request.ccw.cmd_code = TC_READMOD; -	rp->init_request.ccw.flags = CCW_FLAG_SLI; -	rp->init_request.ccw.count = sizeof(rp->init_data); -	rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); -	rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); -	if (rc) -		return rc;  	/* Got a Query Reply */ -	count = sizeof(rp->init_data) - rp->init_request.rescnt;  	uap = (struct raw3270_ua *) (rp->init_data + 1);  	/* Paranoia check. */ -	if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81) -		return -EOPNOTSUPP; +	if (rp->init_readmod.rc || rp->init_data[0] != 0x88 || +	    uap->uab.qcode != 0x81) { +		/* Couldn't detect size. Use default model 2. */ +		rp->model = 2; +		rp->rows = 24; +		rp->cols = 80; +		return; +	}  	/* Copy rows/columns of default Usable Area */  	rp->rows = uap->uab.h;  	rp->cols = uap->uab.w; @@ -668,66 +538,133 @@ __raw3270_size_device(struct raw3270 *rp)  		rp->rows = uap->aua.hauai;  		rp->cols = uap->aua.wauai;  	} -	return 0; +	/* Try to find a model. */ +	rp->model = 0; +	if (rp->rows == 24 && rp->cols == 80) +		rp->model = 2; +	if (rp->rows == 32 && rp->cols == 80) +		rp->model = 3; +	if (rp->rows == 43 && rp->cols == 80) +		rp->model = 4; +	if (rp->rows == 27 && rp->cols == 132) +		rp->model = 5;  } -static int -raw3270_size_device(struct raw3270 *rp) +static void +raw3270_size_device_done(struct raw3270 *rp)  { -	int rc; +	struct raw3270_view *view; -	mutex_lock(&raw3270_init_mutex); -	rp->view = &raw3270_init_view; -	raw3270_init_view.dev = rp; -	if (MACHINE_IS_VM) -		rc = __raw3270_size_device_vm(rp); -	else -		rc = __raw3270_size_device(rp); -	raw3270_init_view.dev = NULL;  	rp->view = NULL; -	mutex_unlock(&raw3270_init_mutex); -	if (rc == 0) {	/* Found something. */ -		/* Try to find a model. */ -		rp->model = 0; -		if (rp->rows == 24 && rp->cols == 80) -			rp->model = 2; -		if (rp->rows == 32 && rp->cols == 80) -			rp->model = 3; -		if (rp->rows == 43 && rp->cols == 80) -			rp->model = 4; -		if (rp->rows == 27 && rp->cols == 132) -			rp->model = 5; -	} else { -		/* Couldn't detect size. Use default model 2. */ -		rp->model = 2; -		rp->rows = 24; -		rp->cols = 80; -		return 0; +	rp->state = RAW3270_STATE_READY; +	/* Notify views about new size */ +	list_for_each_entry(view, &rp->view_list, list) +		if (view->fn->resize) +			view->fn->resize(view, rp->model, rp->rows, rp->cols); +	/* Setup processing done, now activate a view */ +	list_for_each_entry(view, &rp->view_list, list) { +		rp->view = view; +		if (view->fn->activate(view) == 0) +			break; +		rp->view = NULL;  	} +} + +static void +raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +{ +	struct raw3270 *rp = rq->view->dev; + +	raw3270_size_device(rp); +	raw3270_size_device_done(rp); +} + +static void +raw3270_read_modified(struct raw3270 *rp) +{ +	if (rp->state != RAW3270_STATE_W4ATTN) +		return; +	/* Use 'read modified' to get the result of a read partition. */ +	memset(&rp->init_readmod, 0, sizeof(rp->init_readmod)); +	memset(&rp->init_data, 0, sizeof(rp->init_data)); +	rp->init_readmod.ccw.cmd_code = TC_READMOD; +	rp->init_readmod.ccw.flags = CCW_FLAG_SLI; +	rp->init_readmod.ccw.count = sizeof(rp->init_data); +	rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); +	rp->init_readmod.callback = raw3270_read_modified_cb; +	rp->state = RAW3270_STATE_READMOD; +	raw3270_start_irq(&rp->init_view, &rp->init_readmod); +} + +static void +raw3270_writesf_readpart(struct raw3270 *rp) +{ +	static const unsigned char wbuf[] = +		{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + +	/* Store 'read partition' data stream to init_data */ +	memset(&rp->init_readpart, 0, sizeof(rp->init_readpart)); +	memset(&rp->init_data, 0, sizeof(rp->init_data)); +	memcpy(&rp->init_data, wbuf, sizeof(wbuf)); +	rp->init_readpart.ccw.cmd_code = TC_WRITESF; +	rp->init_readpart.ccw.flags = CCW_FLAG_SLI; +	rp->init_readpart.ccw.count = sizeof(wbuf); +	rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data); +	rp->state = RAW3270_STATE_W4ATTN; +	raw3270_start_irq(&rp->init_view, &rp->init_readpart); +} + +/* + * Device reset + */ +static void +raw3270_reset_device_cb(struct raw3270_request *rq, void *data) +{ +	struct raw3270 *rp = rq->view->dev; + +	if (rp->state != RAW3270_STATE_RESET) +		return; +	if (rq->rc) { +		/* Reset command failed. */ +		rp->state = RAW3270_STATE_INIT; +	} else if (MACHINE_IS_VM) { +		raw3270_size_device_vm(rp); +		raw3270_size_device_done(rp); +	} else +		raw3270_writesf_readpart(rp); +	memset(&rp->init_reset, 0, sizeof(rp->init_reset)); +} + +static int +__raw3270_reset_device(struct raw3270 *rp) +{ +	int rc; + +	/* Check if reset is already pending */ +	if (rp->init_reset.view) +		return -EBUSY; +	/* Store reset data stream to init_data/init_reset */ +	rp->init_data[0] = TW_KR; +	rp->init_reset.ccw.cmd_code = TC_EWRITEA; +	rp->init_reset.ccw.flags = CCW_FLAG_SLI; +	rp->init_reset.ccw.count = 1; +	rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data); +	rp->init_reset.callback = raw3270_reset_device_cb; +	rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset); +	if (rc == 0 && rp->state == RAW3270_STATE_INIT) +		rp->state = RAW3270_STATE_RESET;  	return rc;  }  static int  raw3270_reset_device(struct raw3270 *rp)  { +	unsigned long flags;  	int rc; -	mutex_lock(&raw3270_init_mutex); -	memset(&rp->init_request, 0, sizeof(rp->init_request)); -	memset(&rp->init_data, 0, sizeof(rp->init_data)); -	/* Store reset data stream to init_data/init_request */ -	rp->init_data[0] = TW_KR; -	INIT_LIST_HEAD(&rp->init_request.list); -	rp->init_request.ccw.cmd_code = TC_EWRITEA; -	rp->init_request.ccw.flags = CCW_FLAG_SLI; -	rp->init_request.ccw.count = 1; -	rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); -	rp->view = &raw3270_init_view; -	raw3270_init_view.dev = rp; -	rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); -	raw3270_init_view.dev = NULL; -	rp->view = NULL; -	mutex_unlock(&raw3270_init_mutex); +	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); +	rc = __raw3270_reset_device(rp); +	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);  	return rc;  } @@ -741,13 +678,50 @@ raw3270_reset(struct raw3270_view *view)  	if (!rp || rp->view != view ||  	    test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))  		rc = -EACCES; -	else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) -		rc = -ENODEV; +	else if (!raw3270_state_ready(rp)) +		rc = -EBUSY;  	else  		rc = raw3270_reset_device(view->dev);  	return rc;  } +static int +raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, +		 struct irb *irb) +{ +	struct raw3270 *rp; + +	/* +	 * Unit-Check Processing: +	 * Expect Command Reject or Intervention Required. +	 */ +	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { +		/* Request finished abnormally. */ +		if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { +			set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); +			return RAW3270_IO_BUSY; +		} +	} +	if (rq) { +		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { +			if (irb->ecw[0] & SNS0_CMD_REJECT) +				rq->rc = -EOPNOTSUPP; +			else +				rq->rc = -EIO; +		} +	} +	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { +		/* Queue read modified after attention interrupt */ +		rp = view->dev; +		raw3270_read_modified(rp); +	} +	return RAW3270_IO_DONE; +} + +static struct raw3270_fn raw3270_init_fn = { +	.intv = raw3270_init_irq +}; +  /*   * Setup new 3270 device.   */ @@ -776,6 +750,10 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)  	INIT_LIST_HEAD(&rp->req_queue);  	INIT_LIST_HEAD(&rp->view_list); +	rp->init_view.dev = rp; +	rp->init_view.fn = &raw3270_init_fn; +	rp->view = &rp->init_view; +  	/*  	 * Add device to list and find the smallest unused minor  	 * number for it. Note: there is no device with minor 0, @@ -809,31 +787,46 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc)  }  #ifdef CONFIG_TN3270_CONSOLE +/* Tentative definition - see below for actual definition. */ +static struct ccw_driver raw3270_ccw_driver; +  /*   * Setup 3270 device configured as console.   */ -struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) +struct raw3270 __init *raw3270_setup_console(void)  { +	struct ccw_device *cdev; +	unsigned long flags;  	struct raw3270 *rp;  	char *ascebc;  	int rc; +	cdev = ccw_device_create_console(&raw3270_ccw_driver); +	if (IS_ERR(cdev)) +		return ERR_CAST(cdev); +  	rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);  	ascebc = kzalloc(256, GFP_KERNEL);  	rc = raw3270_setup_device(cdev, rp, ascebc);  	if (rc)  		return ERR_PTR(rc);  	set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); -	rc = raw3270_reset_device(rp); -	if (rc) -		return ERR_PTR(rc); -	rc = raw3270_size_device(rp); -	if (rc) -		return ERR_PTR(rc); -	rc = raw3270_reset_device(rp); -	if (rc) + +	rc = ccw_device_enable_console(cdev); +	if (rc) { +		ccw_device_destroy_console(cdev);  		return ERR_PTR(rc); -	set_bit(RAW3270_FLAGS_READY, &rp->flags); +	} + +	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); +	do { +		__raw3270_reset_device(rp); +		while (!raw3270_state_final(rp)) { +			ccw_device_wait_idle(rp->cdev); +			barrier(); +		} +	} while (rp->state != RAW3270_STATE_READY); +	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);  	return rp;  } @@ -843,7 +836,7 @@ raw3270_wait_cons_dev(struct raw3270 *rp)  	unsigned long flags;  	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -	wait_cons_dev(); +	ccw_device_wait_idle(rp->cdev);  	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);  } @@ -859,7 +852,7 @@ raw3270_create_device(struct ccw_device *cdev)  	char *ascebc;  	int rc; -	rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); +	rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);  	if (!rp)  		return ERR_PTR(-ENOMEM);  	ascebc = kmalloc(256, GFP_KERNEL); @@ -895,13 +888,13 @@ raw3270_activate_view(struct raw3270_view *view)  	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);  	if (rp->view == view)  		rc = 0; -	else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) -		rc = -ENODEV; +	else if (!raw3270_state_ready(rp)) +		rc = -EBUSY;  	else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags))  		rc = -EACCES;  	else {  		oldview = NULL; -		if (rp->view) { +		if (rp->view && rp->view->fn->deactivate) {  			oldview = rp->view;  			oldview->fn->deactivate(oldview);  		} @@ -946,7 +939,7 @@ raw3270_deactivate_view(struct raw3270_view *view)  		list_del_init(&view->list);  		list_add_tail(&view->list, &rp->view_list);  		/* Try to activate another view. */ -		if (test_bit(RAW3270_FLAGS_READY, &rp->flags) && +		if (raw3270_state_ready(rp) &&  		    !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {  			list_for_each_entry(view, &rp->view_list, list) {  				rp->view = view; @@ -977,18 +970,16 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor)  		if (rp->minor != minor)  			continue;  		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -		if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { -			atomic_set(&view->ref_count, 2); -			view->dev = rp; -			view->fn = fn; -			view->model = rp->model; -			view->rows = rp->rows; -			view->cols = rp->cols; -			view->ascebc = rp->ascebc; -			spin_lock_init(&view->lock); -			list_add(&view->list, &rp->view_list); -			rc = 0; -		} +		atomic_set(&view->ref_count, 2); +		view->dev = rp; +		view->fn = fn; +		view->model = rp->model; +		view->rows = rp->rows; +		view->cols = rp->cols; +		view->ascebc = rp->ascebc; +		spin_lock_init(&view->lock); +		list_add(&view->list, &rp->view_list); +		rc = 0;  		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);  		break;  	} @@ -1012,14 +1003,11 @@ raw3270_find_view(struct raw3270_fn *fn, int minor)  		if (rp->minor != minor)  			continue;  		spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -		if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { -			view = ERR_PTR(-ENOENT); -			list_for_each_entry(tmp, &rp->view_list, list) { -				if (tmp->fn == fn) { -					raw3270_get_view(tmp); -					view = tmp; -					break; -				} +		list_for_each_entry(tmp, &rp->view_list, list) { +			if (tmp->fn == fn) { +				raw3270_get_view(tmp); +				view = tmp; +				break;  			}  		}  		spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); @@ -1046,7 +1034,7 @@ raw3270_del_view(struct raw3270_view *view)  		rp->view = NULL;  	}  	list_del_init(&view->list); -	if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) && +	if (!rp->view && raw3270_state_ready(rp) &&  	    !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) {  		/* Try to activate another view. */  		list_for_each_entry(nv, &rp->view_list, list) { @@ -1074,10 +1062,6 @@ raw3270_delete_device(struct raw3270 *rp)  	/* Remove from device chain. */  	mutex_lock(&raw3270_mutex); -	if (rp->clttydev && !IS_ERR(rp->clttydev)) -		device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); -	if (rp->cltubdev && !IS_ERR(rp->cltubdev)) -		device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, rp->minor));  	list_del_init(&rp->list);  	mutex_unlock(&raw3270_mutex); @@ -1141,75 +1125,34 @@ static struct attribute_group raw3270_attr_group = {  static int raw3270_create_attributes(struct raw3270 *rp)  { -	int rc; - -	rc = sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); -	if (rc) -		goto out; - -	rp->clttydev = device_create(class3270, &rp->cdev->dev, -				     MKDEV(IBM_TTY3270_MAJOR, rp->minor), NULL, -				     "tty%s", dev_name(&rp->cdev->dev)); -	if (IS_ERR(rp->clttydev)) { -		rc = PTR_ERR(rp->clttydev); -		goto out_ttydev; -	} - -	rp->cltubdev = device_create(class3270, &rp->cdev->dev, -				     MKDEV(IBM_FS3270_MAJOR, rp->minor), NULL, -				     "tub%s", dev_name(&rp->cdev->dev)); -	if (!IS_ERR(rp->cltubdev)) -		goto out; - -	rc = PTR_ERR(rp->cltubdev); -	device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); - -out_ttydev: -	sysfs_remove_group(&rp->cdev->dev.kobj, &raw3270_attr_group); -out: -	return rc; +	return sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group);  }  /*   * Notifier for device addition/removal   */ -struct raw3270_notifier { -	struct list_head list; -	void (*notifier)(int, int); -}; -  static LIST_HEAD(raw3270_notifier); -int raw3270_register_notifier(void (*notifier)(int, int)) +int raw3270_register_notifier(struct raw3270_notifier *notifier)  { -	struct raw3270_notifier *np;  	struct raw3270 *rp; -	np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL); -	if (!np) -		return -ENOMEM; -	np->notifier = notifier;  	mutex_lock(&raw3270_mutex); -	list_add_tail(&np->list, &raw3270_notifier); -	list_for_each_entry(rp, &raw3270_devices, list) { -		get_device(&rp->cdev->dev); -		notifier(rp->minor, 1); -	} +	list_add_tail(¬ifier->list, &raw3270_notifier); +	list_for_each_entry(rp, &raw3270_devices, list) +		notifier->create(rp->minor);  	mutex_unlock(&raw3270_mutex);  	return 0;  } -void raw3270_unregister_notifier(void (*notifier)(int, int)) +void raw3270_unregister_notifier(struct raw3270_notifier *notifier)  { -	struct raw3270_notifier *np; +	struct raw3270 *rp;  	mutex_lock(&raw3270_mutex); -	list_for_each_entry(np, &raw3270_notifier, list) -		if (np->notifier == notifier) { -			list_del(&np->list); -			kfree(np); -			break; -		} +	list_for_each_entry(rp, &raw3270_devices, list) +		notifier->destroy(rp->minor); +	list_del(¬ifier->list);  	mutex_unlock(&raw3270_mutex);  } @@ -1219,29 +1162,20 @@ void raw3270_unregister_notifier(void (*notifier)(int, int))  static int  raw3270_set_online (struct ccw_device *cdev)  { -	struct raw3270 *rp;  	struct raw3270_notifier *np; +	struct raw3270 *rp;  	int rc;  	rp = raw3270_create_device(cdev);  	if (IS_ERR(rp))  		return PTR_ERR(rp); -	rc = raw3270_reset_device(rp); -	if (rc) -		goto failure; -	rc = raw3270_size_device(rp); -	if (rc) -		goto failure; -	rc = raw3270_reset_device(rp); -	if (rc) -		goto failure;  	rc = raw3270_create_attributes(rp);  	if (rc)  		goto failure; -	set_bit(RAW3270_FLAGS_READY, &rp->flags); +	raw3270_reset_device(rp);  	mutex_lock(&raw3270_mutex);  	list_for_each_entry(np, &raw3270_notifier, list) -		np->notifier(rp->minor, 1); +		np->create(rp->minor);  	mutex_unlock(&raw3270_mutex);  	return 0; @@ -1270,14 +1204,14 @@ raw3270_remove (struct ccw_device *cdev)  	 */  	if (rp == NULL)  		return; -	clear_bit(RAW3270_FLAGS_READY, &rp->flags);  	sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group);  	/* Deactivate current view and remove all views. */  	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);  	if (rp->view) { -		rp->view->fn->deactivate(rp->view); +		if (rp->view->fn->deactivate) +			rp->view->fn->deactivate(rp->view);  		rp->view = NULL;  	}  	while (!list_empty(&rp->view_list)) { @@ -1292,7 +1226,7 @@ raw3270_remove (struct ccw_device *cdev)  	mutex_lock(&raw3270_mutex);  	list_for_each_entry(np, &raw3270_notifier, list) -		np->notifier(rp->minor, 0); +		np->destroy(rp->minor);  	mutex_unlock(&raw3270_mutex);  	/* Reset 3270 device. */ @@ -1326,7 +1260,7 @@ static int raw3270_pm_stop(struct ccw_device *cdev)  	if (!rp)  		return 0;  	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); -	if (rp->view) +	if (rp->view && rp->view->fn->deactivate)  		rp->view->fn->deactivate(rp->view);  	if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) {  		/* @@ -1353,7 +1287,7 @@ static int raw3270_pm_start(struct ccw_device *cdev)  		return 0;  	spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);  	clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags); -	if (rp->view) +	if (rp->view && rp->view->fn->activate)  		rp->view->fn->activate(rp->view);  	spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags);  	return 0; @@ -1366,7 +1300,7 @@ void raw3270_pm_unfreeze(struct raw3270_view *view)  	rp = view->dev;  	if (rp && test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) -		ccw_device_force_console(); +		ccw_device_force_console(rp->cdev);  #endif  } @@ -1386,8 +1320,10 @@ static struct ccw_device_id raw3270_id[] = {  };  static struct ccw_driver raw3270_ccw_driver = { -	.name		= "3270", -	.owner		= THIS_MODULE, +	.driver = { +		.name	= "3270", +		.owner	= THIS_MODULE, +	},  	.ids		= raw3270_id,  	.probe		= &raw3270_probe,  	.remove		= &raw3270_remove, @@ -1396,6 +1332,7 @@ static struct ccw_driver raw3270_ccw_driver = {  	.freeze		= &raw3270_pm_stop,  	.thaw		= &raw3270_pm_start,  	.restore	= &raw3270_pm_start, +	.int_class	= IRQIO_C70,  };  static int @@ -1433,6 +1370,7 @@ MODULE_LICENSE("GPL");  module_init(raw3270_init);  module_exit(raw3270_exit); +EXPORT_SYMBOL(class3270);  EXPORT_SYMBOL(raw3270_request_alloc);  EXPORT_SYMBOL(raw3270_request_free);  EXPORT_SYMBOL(raw3270_request_reset); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index ed34eb2199c..e1e41c2861f 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -91,6 +91,7 @@ struct raw3270_iocb {  struct raw3270;  struct raw3270_view; +extern struct class *class3270;  /* 3270 CCW request */  struct raw3270_request { @@ -140,6 +141,7 @@ struct raw3270_fn {  		     struct raw3270_request *, struct irb *);  	void (*release)(struct raw3270_view *);  	void (*free)(struct raw3270_view *); +	void (*resize)(struct raw3270_view *, int, int, int);  };  /* @@ -171,6 +173,7 @@ int raw3270_start_locked(struct raw3270_view *, struct raw3270_request *);  int raw3270_start_irq(struct raw3270_view *, struct raw3270_request *);  int raw3270_reset(struct raw3270_view *);  struct raw3270_view *raw3270_view(struct raw3270_view *); +int raw3270_view_active(struct raw3270_view *);  /* Reference count inliner for view structures. */  static inline void @@ -188,12 +191,18 @@ raw3270_put_view(struct raw3270_view *view)  		wake_up(&raw3270_wait_queue);  } -struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); +struct raw3270 *raw3270_setup_console(void);  void raw3270_wait_cons_dev(struct raw3270 *);  /* Notifier for device addition/removal */ -int raw3270_register_notifier(void (*notifier)(int, int)); -void raw3270_unregister_notifier(void (*notifier)(int, int)); +struct raw3270_notifier { +	struct list_head list; +	void (*create)(int minor); +	void (*destroy)(int minor); +}; + +int raw3270_register_notifier(struct raw3270_notifier *); +void raw3270_unregister_notifier(struct raw3270_notifier *);  void raw3270_pm_unfreeze(struct raw3270_view *);  /* diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 35cc4686b99..c316051d9bd 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -7,6 +7,7 @@   *	      Martin Schwidefsky <schwidefsky@de.ibm.com>   */ +#include <linux/kernel_stat.h>  #include <linux/module.h>  #include <linux/err.h>  #include <linux/spinlock.h> @@ -19,15 +20,12 @@  #include <linux/completion.h>  #include <linux/platform_device.h>  #include <asm/types.h> -#include <asm/s390_ext.h> +#include <asm/irq.h>  #include "sclp.h"  #define SCLP_HEADER		"sclp: " -/* Structure for register_early_external_interrupt. */ -static ext_int_info_t ext_int_info_hwc; -  /* Lock to protect internal data consistency. */  static DEFINE_SPINLOCK(sclp_lock); @@ -52,16 +50,50 @@ static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));  /* Suspend request */  static DECLARE_COMPLETION(sclp_request_queue_flushed); +/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ +int sclp_console_pages = SCLP_CONSOLE_PAGES; +/* Flag to indicate if buffer pages are dropped on buffer full condition */ +int sclp_console_drop = 0; +/* Number of times the console dropped buffer pages */ +unsigned long sclp_console_full; +  static void sclp_suspend_req_cb(struct sclp_req *req, void *data)  {  	complete(&sclp_request_queue_flushed);  } +static int __init sclp_setup_console_pages(char *str) +{ +	int pages, rc; + +	rc = kstrtoint(str, 0, &pages); +	if (!rc && pages >= SCLP_CONSOLE_PAGES) +		sclp_console_pages = pages; +	return 1; +} + +__setup("sclp_con_pages=", sclp_setup_console_pages); + +static int __init sclp_setup_console_drop(char *str) +{ +	int drop, rc; + +	rc = kstrtoint(str, 0, &drop); +	if (!rc && drop) +		sclp_console_drop = 1; +	return 1; +} + +__setup("sclp_con_drop=", sclp_setup_console_drop); +  static struct sclp_req sclp_suspend_req;  /* Timer for request retries. */  static struct timer_list sclp_request_timer; +/* Timer for queued requests. */ +static struct timer_list sclp_queue_timer; +  /* Internal state: is the driver initialized? */  static volatile enum sclp_init_state_t {  	sclp_init_state_uninitialized, @@ -119,14 +151,19 @@ static int sclp_init(void);  int  sclp_service_call(sclp_cmdw_t command, void *sccb)  { -	int cc; +	int cc = 4; /* Initialize for program check handling */  	asm volatile( -		"	.insn	rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */ -		"	ipm	%0\n" -		"	srl	%0,28" -		: "=&d" (cc) : "d" (command), "a" (__pa(sccb)) +		"0:	.insn	rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */ +		"1:	ipm	%0\n" +		"	srl	%0,28\n" +		"2:\n" +		EX_TABLE(0b, 2b) +		EX_TABLE(1b, 2b) +		: "+&d" (cc) : "d" (command), "a" (__pa(sccb))  		: "cc", "memory"); +	if (cc == 4) +		return -EINVAL;  	if (cc == 3)  		return -EIO;  	if (cc == 2) @@ -181,6 +218,76 @@ sclp_request_timeout(unsigned long data)  	sclp_process_queue();  } +/* + * Returns the expire value in jiffies of the next pending request timeout, + * if any. Needs to be called with sclp_lock. + */ +static unsigned long __sclp_req_queue_find_next_timeout(void) +{ +	unsigned long expires_next = 0; +	struct sclp_req *req; + +	list_for_each_entry(req, &sclp_req_queue, list) { +		if (!req->queue_expires) +			continue; +		if (!expires_next || +		   (time_before(req->queue_expires, expires_next))) +				expires_next = req->queue_expires; +	} +	return expires_next; +} + +/* + * Returns expired request, if any, and removes it from the list. + */ +static struct sclp_req *__sclp_req_queue_remove_expired_req(void) +{ +	unsigned long flags, now; +	struct sclp_req *req; + +	spin_lock_irqsave(&sclp_lock, flags); +	now = jiffies; +	/* Don't need list_for_each_safe because we break out after list_del */ +	list_for_each_entry(req, &sclp_req_queue, list) { +		if (!req->queue_expires) +			continue; +		if (time_before_eq(req->queue_expires, now)) { +			if (req->status == SCLP_REQ_QUEUED) { +				req->status = SCLP_REQ_QUEUED_TIMEOUT; +				list_del(&req->list); +				goto out; +			} +		} +	} +	req = NULL; +out: +	spin_unlock_irqrestore(&sclp_lock, flags); +	return req; +} + +/* + * Timeout handler for queued requests. Removes request from list and + * invokes callback. This timer can be set per request in situations where + * waiting too long would be harmful to the system, e.g. during SE reboot. + */ +static void sclp_req_queue_timeout(unsigned long data) +{ +	unsigned long flags, expires_next; +	struct sclp_req *req; + +	do { +		req = __sclp_req_queue_remove_expired_req(); +		if (req && req->callback) +			req->callback(req, req->callback_data); +	} while (req); + +	spin_lock_irqsave(&sclp_lock, flags); +	expires_next = __sclp_req_queue_find_next_timeout(); +	if (expires_next) +		mod_timer(&sclp_queue_timer, expires_next); +	spin_unlock_irqrestore(&sclp_lock, flags); +} +  /* Try to start a request. Return zero if the request was successfully   * started or if it will be started at a later time. Return non-zero otherwise.   * Called while sclp_lock is locked. */ @@ -283,6 +390,13 @@ sclp_add_request(struct sclp_req *req)  	req->start_count = 0;  	list_add_tail(&req->list, &sclp_req_queue);  	rc = 0; +	if (req->queue_timeout) { +		req->queue_expires = jiffies + req->queue_timeout * HZ; +		if (!timer_pending(&sclp_queue_timer) || +		    time_after(sclp_queue_timer.expires, req->queue_expires)) +			mod_timer(&sclp_queue_timer, req->queue_expires); +	} else +		req->queue_expires = 0;  	/* Start if request is first in list */  	if (sclp_running_state == sclp_running_state_idle &&  	    req->list.prev == &sclp_req_queue) { @@ -336,7 +450,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)  			reg->receiver_fn(evbuf);  			spin_lock_irqsave(&sclp_lock, flags);  		} else if (reg == NULL) -			rc = -ENOSYS; +			rc = -EOPNOTSUPP;  	}  	spin_unlock_irqrestore(&sclp_lock, flags);  	return rc; @@ -395,13 +509,14 @@ __sclp_find_req(u32 sccb)  /* Handler for external interruption. Perform request post-processing.   * Prepare read event data request if necessary. Start processing of next   * request on queue. */ -static void sclp_interrupt_handler(unsigned int ext_int_code, +static void sclp_interrupt_handler(struct ext_code ext_code,  				   unsigned int param32, unsigned long param64)  {  	struct sclp_req *req;  	u32 finished_sccb;  	u32 evbuf_pending; +	inc_irq_stat(IRQEXT_SCP);  	spin_lock(&sclp_lock);  	finished_sccb = param32 & 0xfffffff8;  	evbuf_pending = param32 & 0x3; @@ -451,7 +566,7 @@ sclp_sync_wait(void)  	timeout = 0;  	if (timer_pending(&sclp_request_timer)) {  		/* Get timeout TOD value */ -		timeout = get_clock() + +		timeout = get_tod_clock_fast() +  			  sclp_tod_from_jiffies(sclp_request_timer.expires -  						jiffies);  	} @@ -473,7 +588,7 @@ sclp_sync_wait(void)  	while (sclp_running_state != sclp_running_state_idle) {  		/* Check for expired request timer */  		if (timer_pending(&sclp_request_timer) && -		    get_clock() > timeout && +		    get_tod_clock_fast() > timeout &&  		    del_timer(&sclp_request_timer))  			sclp_request_timer.function(sclp_request_timer.data);  		cpu_relax(); @@ -655,16 +770,6 @@ sclp_remove_processed(struct sccb_header *sccb)  EXPORT_SYMBOL(sclp_remove_processed); -struct init_sccb { -	struct sccb_header header; -	u16 _reserved; -	u16 mask_length; -	sccb_mask_t receive_mask; -	sccb_mask_t send_mask; -	sccb_mask_t sclp_receive_mask; -	sccb_mask_t sclp_send_mask; -} __attribute__((packed)); -  /* Prepare init mask request. Called while sclp_lock is locked. */  static inline void  __sclp_make_init_req(u32 receive_mask, u32 send_mask) @@ -819,11 +924,12 @@ EXPORT_SYMBOL(sclp_reactivate);  /* Handler for external interruption used during initialization. Modify   * request state to done. */ -static void sclp_check_handler(unsigned int ext_int_code, +static void sclp_check_handler(struct ext_code ext_code,  			       unsigned int param32, unsigned long param64)  {  	u32 finished_sccb; +	inc_irq_stat(IRQEXT_SCP);  	finished_sccb = param32 & 0xfffffff8;  	/* Is this the interrupt we are waiting for? */  	if (finished_sccb == 0) @@ -866,8 +972,7 @@ sclp_check_interface(void)  	spin_lock_irqsave(&sclp_lock, flags);  	/* Prepare init mask command */ -	rc = register_early_external_interrupt(0x2401, sclp_check_handler, -					       &ext_int_info_hwc); +	rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);  	if (rc) {  		spin_unlock_irqrestore(&sclp_lock, flags);  		return rc; @@ -885,12 +990,12 @@ sclp_check_interface(void)  		spin_unlock_irqrestore(&sclp_lock, flags);  		/* Enable service-signal interruption - needs to happen  		 * with IRQs enabled. */ -		ctl_set_bit(0, 9); +		irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);  		/* Wait for signal from interrupt or timeout */  		sclp_sync_wait();  		/* Disable service-signal interruption - needs to happen  		 * with IRQs enabled. */ -		ctl_clear_bit(0,9); +		irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);  		spin_lock_irqsave(&sclp_lock, flags);  		del_timer(&sclp_request_timer);  		if (sclp_init_req.status == SCLP_REQ_DONE && @@ -900,8 +1005,7 @@ sclp_check_interface(void)  		} else  			rc = -EBUSY;  	} -	unregister_early_external_interrupt(0x2401, sclp_check_handler, -					    &ext_int_info_hwc); +	unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);  	spin_unlock_irqrestore(&sclp_lock, flags);  	return rc;  } @@ -1025,11 +1129,47 @@ static const struct dev_pm_ops sclp_pm_ops = {  	.restore	= sclp_restore,  }; +static ssize_t sclp_show_console_pages(struct device_driver *dev, char *buf) +{ +	return sprintf(buf, "%i\n", sclp_console_pages); +} + +static DRIVER_ATTR(con_pages, S_IRUSR, sclp_show_console_pages, NULL); + +static ssize_t sclp_show_con_drop(struct device_driver *dev, char *buf) +{ +	return sprintf(buf, "%i\n", sclp_console_drop); +} + +static DRIVER_ATTR(con_drop, S_IRUSR, sclp_show_con_drop, NULL); + +static ssize_t sclp_show_console_full(struct device_driver *dev, char *buf) +{ +	return sprintf(buf, "%lu\n", sclp_console_full); +} + +static DRIVER_ATTR(con_full, S_IRUSR, sclp_show_console_full, NULL); + +static struct attribute *sclp_drv_attrs[] = { +	&driver_attr_con_pages.attr, +	&driver_attr_con_drop.attr, +	&driver_attr_con_full.attr, +	NULL, +}; +static struct attribute_group sclp_drv_attr_group = { +	.attrs = sclp_drv_attrs, +}; +static const struct attribute_group *sclp_drv_attr_groups[] = { +	&sclp_drv_attr_group, +	NULL, +}; +  static struct platform_driver sclp_pdrv = {  	.driver = {  		.name	= "sclp",  		.owner	= THIS_MODULE,  		.pm	= &sclp_pm_ops, +		.groups = sclp_drv_attr_groups,  	},  }; @@ -1053,6 +1193,8 @@ sclp_init(void)  	INIT_LIST_HEAD(&sclp_reg_list);  	list_add(&sclp_state_change_event.list, &sclp_reg_list);  	init_timer(&sclp_request_timer); +	init_timer(&sclp_queue_timer); +	sclp_queue_timer.function = sclp_req_queue_timeout;  	/* Check interface */  	spin_unlock_irqrestore(&sclp_lock, flags);  	rc = sclp_check_interface(); @@ -1064,15 +1206,14 @@ sclp_init(void)  	if (rc)  		goto fail_init_state_uninitialized;  	/* Register interrupt handler */ -	rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, -					       &ext_int_info_hwc); +	rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_interrupt_handler);  	if (rc)  		goto fail_unregister_reboot_notifier;  	sclp_init_state = sclp_init_state_initialized;  	spin_unlock_irqrestore(&sclp_lock, flags);  	/* Enable service-signal external interruption - needs to happen with  	 * IRQs enabled. */ -	ctl_set_bit(0, 9); +	irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);  	sclp_init_mask(1);  	return 0; @@ -1109,10 +1250,12 @@ static __init int sclp_initcall(void)  	rc = platform_driver_register(&sclp_pdrv);  	if (rc)  		return rc; +  	sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); -	rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; +	rc = PTR_ERR_OR_ZERO(sclp_pdev);  	if (rc)  		goto fail_platform_driver_unregister; +  	rc = atomic_notifier_chain_register(&panic_notifier_list,  					    &sclp_on_panic_nb);  	if (rc) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 6bb5a6bdfab..a68b5ec7d04 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -1,5 +1,5 @@  /* - * Copyright IBM Corp. 1999, 2009 + * Copyright IBM Corp. 1999,2012   *   * Author(s): Martin Peschke <mpeschke@de.ibm.com>   *	      Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -15,7 +15,7 @@  /* maximum number of pages concerning our own memory management */  #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) -#define MAX_CONSOLE_PAGES	6 +#define SCLP_CONSOLE_PAGES	6  #define EVTYP_OPCMD		0x01  #define EVTYP_MSG		0x02 @@ -28,6 +28,7 @@  #define EVTYP_CONFMGMDATA	0x04  #define EVTYP_SDIAS		0x1C  #define EVTYP_ASYNC		0x0A +#define EVTYP_OCF		0x1E  #define EVTYP_OPCMD_MASK	0x80000000  #define EVTYP_MSG_MASK		0x40000000 @@ -40,6 +41,7 @@  #define EVTYP_CONFMGMDATA_MASK	0x10000000  #define EVTYP_SDIAS_MASK	0x00000010  #define EVTYP_ASYNC_MASK	0x00400000 +#define EVTYP_OCF_MASK		0x00000004  #define GNRLMSGFLGS_DOM		0x8000  #define GNRLMSGFLGS_SNDALRM	0x4000 @@ -86,11 +88,23 @@ struct sccb_header {  	u16	response_code;  } __attribute__((packed)); +struct init_sccb { +	struct sccb_header header; +	u16 _reserved; +	u16 mask_length; +	sccb_mask_t receive_mask; +	sccb_mask_t send_mask; +	sccb_mask_t sclp_receive_mask; +	sccb_mask_t sclp_send_mask; +} __attribute__((packed)); +  extern u64 sclp_facilities; +  #define SCLP_HAS_CHP_INFO	(sclp_facilities & 0x8000000000000000ULL)  #define SCLP_HAS_CHP_RECONFIG	(sclp_facilities & 0x2000000000000000ULL)  #define SCLP_HAS_CPU_INFO	(sclp_facilities & 0x0800000000000000ULL)  #define SCLP_HAS_CPU_RECONFIG	(sclp_facilities & 0x0400000000000000ULL) +#define SCLP_HAS_PCI_RECONFIG	(sclp_facilities & 0x0000000040000000ULL)  struct gds_subvector { @@ -119,6 +133,11 @@ struct sclp_req {  	/* Callback that is called after reaching final status. */  	void (*callback)(struct sclp_req *, void *data);  	void *callback_data; +	int queue_timeout;		/* request queue timeout (sec), set by +					   caller of sclp_add_request(), if +					   needed */ +	/* Internal fields */ +	unsigned long queue_expires;	/* request queue timeout (jiffies) */  };  #define SCLP_REQ_FILLED	  0x00	/* request is ready to be processed */ @@ -126,6 +145,9 @@ struct sclp_req {  #define SCLP_REQ_RUNNING  0x02	/* request is currently running */  #define SCLP_REQ_DONE	  0x03	/* request is completed successfully */  #define SCLP_REQ_FAILED	  0x05	/* request is finally failed */ +#define SCLP_REQ_QUEUED_TIMEOUT 0x06	/* request on queue timed out */ + +#define SCLP_QUEUE_INTERVAL 5	/* timeout interval for request queue */  /* function pointers that a high level driver has to use for registration */  /* of some routines it wants to be called from the low level driver */ @@ -158,10 +180,19 @@ int sclp_remove_processed(struct sccb_header *sccb);  int sclp_deactivate(void);  int sclp_reactivate(void);  int sclp_service_call(sclp_cmdw_t command, void *sccb); +int sclp_sync_request(sclp_cmdw_t command, void *sccb); +int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout);  int sclp_sdias_init(void);  void sclp_sdias_exit(void); +extern int sclp_console_pages; +extern int sclp_console_drop; +extern unsigned long sclp_console_full; +extern u8 sclp_fac84; +extern unsigned long long sclp_rzm; +extern unsigned long long sclp_rnmax; +  /* useful inlines */  /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ @@ -186,4 +217,26 @@ sclp_ascebc_str(unsigned char *str, int nr)  	(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);  } +static inline struct gds_vector * +sclp_find_gds_vector(void *start, void *end, u16 id) +{ +	struct gds_vector *v; + +	for (v = start; (void *) v < end; v = (void *) v + v->length) +		if (v->gds_id == id) +			return v; +	return NULL; +} + +static inline struct gds_subvector * +sclp_find_gds_subvector(void *start, void *end, u8 key) +{ +	struct gds_subvector *sv; + +	for (sv = start; (void *) sv < end; sv = (void *) sv + sv->length) +		if (sv->key == key) +			return sv; +	return NULL; +} +  #endif	 /* __SCLP_H__ */ diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c index 7ad30e72f86..5f9f929e891 100644 --- a/drivers/s390/char/sclp_async.c +++ b/drivers/s390/char/sclp_async.c @@ -82,12 +82,9 @@ static int proc_handler_callhome(struct ctl_table *ctl, int write,  			return -EFAULT;  	} else {  		len = *count; -		rc = copy_from_user(buf, buffer, sizeof(buf)); -		if (rc != 0) -			return -EFAULT; -		buf[sizeof(buf) - 1] = '\0'; -		if (strict_strtoul(buf, 0, &val) != 0) -			return -EINVAL; +		rc = kstrtoul_from_user(buffer, len, 0, &val); +		if (rc) +			return rc;  		if (val != 0 && val != 1)  			return -EINVAL;  		callhome_enabled = val; diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 4b60ede07f0..6e14999f9e8 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -1,5 +1,5 @@  /* - * Copyright IBM Corp. 2007, 2009 + * Copyright IBM Corp. 2007,2012   *   * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,   *	      Peter Oberparleiter <peter.oberparleiter@de.ibm.com> @@ -12,137 +12,22 @@  #include <linux/init.h>  #include <linux/errno.h>  #include <linux/err.h> +#include <linux/export.h>  #include <linux/slab.h>  #include <linux/string.h>  #include <linux/mm.h>  #include <linux/mmzone.h>  #include <linux/memory.h> +#include <linux/module.h>  #include <linux/platform_device.h> +#include <asm/ctl_reg.h>  #include <asm/chpid.h> -#include <asm/sclp.h>  #include <asm/setup.h> +#include <asm/page.h> +#include <asm/sclp.h>  #include "sclp.h" -#define SCLP_CMDW_READ_SCP_INFO		0x00020001 -#define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001 - -struct read_info_sccb { -	struct	sccb_header header;	/* 0-7 */ -	u16	rnmax;			/* 8-9 */ -	u8	rnsize;			/* 10 */ -	u8	_reserved0[24 - 11];	/* 11-15 */ -	u8	loadparm[8];		/* 24-31 */ -	u8	_reserved1[48 - 32];	/* 32-47 */ -	u64	facilities;		/* 48-55 */ -	u8	_reserved2[84 - 56];	/* 56-83 */ -	u8	fac84;			/* 84 */ -	u8	_reserved3[91 - 85];	/* 85-90 */ -	u8	flags;			/* 91 */ -	u8	_reserved4[100 - 92];	/* 92-99 */ -	u32	rnsize2;		/* 100-103 */ -	u64	rnmax2;			/* 104-111 */ -	u8	_reserved5[4096 - 112];	/* 112-4095 */ -} __attribute__((packed, aligned(PAGE_SIZE))); - -static struct read_info_sccb __initdata early_read_info_sccb; -static int __initdata early_read_info_sccb_valid; - -u64 sclp_facilities; -static u8 sclp_fac84; -static unsigned long long rzm; -static unsigned long long rnmax; - -static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) -{ -	int rc; - -	__ctl_set_bit(0, 9); -	rc = sclp_service_call(cmd, sccb); -	if (rc) -		goto out; -	__load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT | -			PSW_MASK_WAIT | PSW_DEFAULT_KEY); -	local_irq_disable(); -out: -	/* Contents of the sccb might have changed. */ -	barrier(); -	__ctl_clear_bit(0, 9); -	return rc; -} - -static void __init sclp_read_info_early(void) -{ -	int rc; -	int i; -	struct read_info_sccb *sccb; -	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, -				  SCLP_CMDW_READ_SCP_INFO}; - -	sccb = &early_read_info_sccb; -	for (i = 0; i < ARRAY_SIZE(commands); i++) { -		do { -			memset(sccb, 0, sizeof(*sccb)); -			sccb->header.length = sizeof(*sccb); -			sccb->header.function_code = 0x80; -			sccb->header.control_mask[2] = 0x80; -			rc = sclp_cmd_sync_early(commands[i], sccb); -		} while (rc == -EBUSY); - -		if (rc) -			break; -		if (sccb->header.response_code == 0x10) { -			early_read_info_sccb_valid = 1; -			break; -		} -		if (sccb->header.response_code != 0x1f0) -			break; -	} -} - -void __init sclp_facilities_detect(void) -{ -	struct read_info_sccb *sccb; - -	sclp_read_info_early(); -	if (!early_read_info_sccb_valid) -		return; - -	sccb = &early_read_info_sccb; -	sclp_facilities = sccb->facilities; -	sclp_fac84 = sccb->fac84; -	rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; -	rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; -	rzm <<= 20; -} - -unsigned long long sclp_get_rnmax(void) -{ -	return rnmax; -} - -unsigned long long sclp_get_rzm(void) -{ -	return rzm; -} - -/* - * This function will be called after sclp_facilities_detect(), which gets - * called from early.c code. Therefore the sccb should have valid contents. - */ -void __init sclp_get_ipl_info(struct sclp_ipl_info *info) -{ -	struct read_info_sccb *sccb; - -	if (!early_read_info_sccb_valid) -		return; -	sccb = &early_read_info_sccb; -	info->is_valid = 1; -	if (sccb->flags & 0x2) -		info->has_dump = 1; -	memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN); -} -  static void sclp_sync_callback(struct sclp_req *req, void *data)  {  	struct completion *completion = data; @@ -150,7 +35,12 @@ static void sclp_sync_callback(struct sclp_req *req, void *data)  	complete(completion);  } -static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) +{ +	return sclp_sync_request_timeout(cmd, sccb, 0); +} + +int sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout)  {  	struct completion completion;  	struct sclp_req *request; @@ -159,6 +49,8 @@ static int do_sync_request(sclp_cmdw_t cmd, void *sccb)  	request = kzalloc(sizeof(*request), GFP_KERNEL);  	if (!request)  		return -ENOMEM; +	if (timeout) +		request->queue_timeout = timeout;  	request->command = cmd;  	request->sccb = sccb;  	request->status = SCLP_REQ_FILLED; @@ -225,7 +117,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)  	if (!sccb)  		return -ENOMEM;  	sccb->header.length = sizeof(*sccb); -	rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); +	rc = sclp_sync_request_timeout(SCLP_CMDW_READ_CPU_INFO, sccb, +				       SCLP_QUEUE_INTERVAL);  	if (rc)  		goto out;  	if (sccb->header.response_code != 0x0010) { @@ -259,7 +152,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd)  	if (!sccb)  		return -ENOMEM;  	sccb->header.length = sizeof(*sccb); -	rc = do_sync_request(cmd, sccb); +	rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);  	if (rc)  		goto out;  	switch (sccb->header.response_code) { @@ -300,7 +193,6 @@ struct memory_increment {  	struct list_head list;  	u16 rn;  	int standby; -	int usecount;  };  struct assign_storage_sccb { @@ -310,14 +202,14 @@ struct assign_storage_sccb {  int arch_get_memory_phys_device(unsigned long start_pfn)  { -	if (!rzm) +	if (!sclp_rzm)  		return 0; -	return PFN_PHYS(start_pfn) >> ilog2(rzm); +	return PFN_PHYS(start_pfn) >> ilog2(sclp_rzm);  }  static unsigned long long rn2addr(u16 rn)  { -	return (unsigned long long) (rn - 1) * rzm; +	return (unsigned long long) (rn - 1) * sclp_rzm;  }  static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) @@ -330,7 +222,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)  		return -ENOMEM;  	sccb->header.length = PAGE_SIZE;  	sccb->rn = rn; -	rc = do_sync_request(cmd, sccb); +	rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);  	if (rc)  		goto out;  	switch (sccb->header.response_code) { @@ -351,7 +243,15 @@ out:  static int sclp_assign_storage(u16 rn)  { -	return do_assign_storage(0x000d0001, rn); +	unsigned long long start; +	int rc; + +	rc = do_assign_storage(0x000d0001, rn); +	if (rc) +		return rc; +	start = rn2addr(rn); +	storage_key_init_range(start, start + sclp_rzm); +	return 0;  }  static int sclp_unassign_storage(u16 rn) @@ -377,14 +277,17 @@ static int sclp_attach_storage(u8 id)  	if (!sccb)  		return -ENOMEM;  	sccb->header.length = PAGE_SIZE; -	rc = do_sync_request(0x00080001 | id << 8, sccb); +	rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb, +				       SCLP_QUEUE_INTERVAL);  	if (rc)  		goto out;  	switch (sccb->header.response_code) {  	case 0x0020:  		set_bit(id, sclp_storage_ids); -		for (i = 0; i < sccb->assigned; i++) -			sclp_unassign_storage(sccb->entries[i] >> 16); +		for (i = 0; i < sccb->assigned; i++) { +			if (sccb->entries[i]) +				sclp_unassign_storage(sccb->entries[i] >> 16); +		}  		break;  	default:  		rc = -EIO; @@ -406,23 +309,12 @@ static int sclp_mem_change_state(unsigned long start, unsigned long size,  		istart = rn2addr(incr->rn);  		if (start + size - 1 < istart)  			break; -		if (start > istart + rzm - 1) +		if (start > istart + sclp_rzm - 1)  			continue; -		if (online) { -			if (incr->usecount++) -				continue; -			/* -			 * Don't break the loop if one assign fails. Loop may -			 * be walked again on CANCEL and we can't save -			 * information if state changed before or not. -			 * So continue and increase usecount for all increments. -			 */ +		if (online)  			rc |= sclp_assign_storage(incr->rn); -		} else { -			if (--incr->usecount) -				continue; +		else  			sclp_unassign_storage(incr->rn); -		}  	}  	return rc ? -EIO : 0;  } @@ -439,9 +331,8 @@ static int sclp_mem_notifier(struct notifier_block *nb,  	start = arg->start_pfn << PAGE_SHIFT;  	size = arg->nr_pages << PAGE_SHIFT;  	mutex_lock(&sclp_mem_mutex); -	for (id = 0; id <= sclp_max_storage_id; id++) -		if (!test_bit(id, sclp_storage_ids)) -			sclp_attach_storage(id); +	for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1) +		sclp_attach_storage(id);  	switch (action) {  	case MEM_ONLINE:  	case MEM_GOING_OFFLINE: @@ -482,7 +373,7 @@ static void __init add_memory_merged(u16 rn)  	if (!first_rn)  		goto skip_add;  	start = rn2addr(first_rn); -	size = (unsigned long long ) num * rzm; +	size = (unsigned long long) num * sclp_rzm;  	if (start >= VMEM_MAX_PHYS)  		goto skip_add;  	if (start + size > VMEM_MAX_PHYS) @@ -530,7 +421,7 @@ static void __init insert_increment(u16 rn, int standby, int assigned)  	}  	if (!assigned)  		new_incr->rn = last_rn + 1; -	if (new_incr->rn > rnmax) { +	if (new_incr->rn > sclp_rnmax) {  		kfree(new_incr);  		return;  	} @@ -571,7 +462,7 @@ static int __init sclp_detect_standby_memory(void)  	struct read_storage_sccb *sccb;  	int i, id, assigned, rc; -	if (!early_read_info_sccb_valid) +	if (OLDMEM_BASE) /* No standby memory in kdump mode */  		return 0;  	if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)  		return 0; @@ -583,7 +474,7 @@ static int __init sclp_detect_standby_memory(void)  	for (id = 0; id <= sclp_max_storage_id; id++) {  		memset(sccb, 0, PAGE_SIZE);  		sccb->header.length = PAGE_SIZE; -		rc = do_sync_request(0x00040001 | id << 8, sccb); +		rc = sclp_sync_request(0x00040001 | id << 8, sccb);  		if (rc)  			goto out;  		switch (sccb->header.response_code) { @@ -615,7 +506,7 @@ static int __init sclp_detect_standby_memory(void)  	}  	if (rc || list_empty(&sclp_mem_list))  		goto out; -	for (i = 1; i <= rnmax - assigned; i++) +	for (i = 1; i <= sclp_rnmax - assigned; i++)  		insert_increment(0, 1, 0);  	rc = register_memory_notifier(&sclp_mem_nb);  	if (rc) @@ -624,7 +515,7 @@ static int __init sclp_detect_standby_memory(void)  	if (rc)  		goto out;  	sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); -	rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; +	rc = PTR_ERR_OR_ZERO(sclp_pdev);  	if (rc)  		goto out_driver;  	sclp_add_standby_memory(); @@ -640,6 +531,67 @@ __initcall(sclp_detect_standby_memory);  #endif /* CONFIG_MEMORY_HOTPLUG */  /* + * PCI I/O adapter configuration related functions. + */ +#define SCLP_CMDW_CONFIGURE_PCI			0x001a0001 +#define SCLP_CMDW_DECONFIGURE_PCI		0x001b0001 + +#define SCLP_RECONFIG_PCI_ATPYE			2 + +struct pci_cfg_sccb { +	struct sccb_header header; +	u8 atype;		/* adapter type */ +	u8 reserved1; +	u16 reserved2; +	u32 aid;		/* adapter identifier */ +} __packed; + +static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) +{ +	struct pci_cfg_sccb *sccb; +	int rc; + +	if (!SCLP_HAS_PCI_RECONFIG) +		return -EOPNOTSUPP; + +	sccb = (struct pci_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); +	if (!sccb) +		return -ENOMEM; + +	sccb->header.length = PAGE_SIZE; +	sccb->atype = SCLP_RECONFIG_PCI_ATPYE; +	sccb->aid = fid; +	rc = sclp_sync_request(cmd, sccb); +	if (rc) +		goto out; +	switch (sccb->header.response_code) { +	case 0x0020: +	case 0x0120: +		break; +	default: +		pr_warn("configure PCI I/O adapter failed: cmd=0x%08x  response=0x%04x\n", +			cmd, sccb->header.response_code); +		rc = -EIO; +		break; +	} +out: +	free_page((unsigned long) sccb); +	return rc; +} + +int sclp_pci_configure(u32 fid) +{ +	return do_pci_configure(SCLP_CMDW_CONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_configure); + +int sclp_pci_deconfigure(u32 fid) +{ +	return do_pci_configure(SCLP_CMDW_DECONFIGURE_PCI, fid); +} +EXPORT_SYMBOL(sclp_pci_deconfigure); + +/*   * Channel path configuration related functions.   */ @@ -666,7 +618,7 @@ static int do_chp_configure(sclp_cmdw_t cmd)  	if (!sccb)  		return -ENOMEM;  	sccb->header.length = sizeof(*sccb); -	rc = do_sync_request(cmd, sccb); +	rc = sclp_sync_request(cmd, sccb);  	if (rc)  		goto out;  	switch (sccb->header.response_code) { @@ -741,7 +693,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info)  	if (!sccb)  		return -ENOMEM;  	sccb->header.length = sizeof(*sccb); -	rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); +	rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);  	if (rc)  		goto out;  	if (sccb->header.response_code != 0x0010) { @@ -757,3 +709,8 @@ out:  	free_page((unsigned long) sccb);  	return rc;  } + +bool sclp_has_sprp(void) +{ +	return !!(sclp_fac84 & 0x2); +} diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index ecf45c54f8c..5880def98fc 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -130,6 +130,31 @@ sclp_console_timeout(unsigned long data)  }  /* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_console_drop_buffer(void) +{ +	struct list_head *list; +	struct sclp_buffer *buffer; +	void *page; + +	if (!sclp_console_drop) +		return 0; +	list = sclp_con_outqueue.next; +	if (sclp_con_queue_running) +		/* The first element is in I/O */ +		list = list->next; +	if (list == &sclp_con_outqueue) +		return 0; +	list_del(list); +	buffer = list_entry(list, struct sclp_buffer, list); +	page = sclp_unmake_buffer(buffer); +	list_add_tail((struct list_head *) page, &sclp_con_pages); +	return 1; +} + +/*   * Writes the given message to S390 system console   */  static void @@ -150,9 +175,13 @@ sclp_console_write(struct console *console, const char *message,  	do {  		/* make sure we have a console output buffer */  		if (sclp_conbuf == NULL) { +			if (list_empty(&sclp_con_pages)) +				sclp_console_full++;  			while (list_empty(&sclp_con_pages)) {  				if (sclp_con_suspended)  					goto out; +				if (sclp_console_drop_buffer()) +					break;  				spin_unlock_irqrestore(&sclp_con_lock, flags);  				sclp_sync_wait();  				spin_lock_irqsave(&sclp_con_lock, flags); @@ -297,7 +326,7 @@ sclp_console_init(void)  		return rc;  	/* Allocate pages for output buffering */  	INIT_LIST_HEAD(&sclp_con_pages); -	for (i = 0; i < MAX_CONSOLE_PAGES; i++) { +	for (i = 0; i < sclp_console_pages; i++) {  		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);  		list_add_tail(page, &sclp_con_pages);  	} diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index b497afe061c..94415620747 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -1,6 +1,4 @@  /* - *  drivers/s390/char/sclp_config.c - *   *    Copyright IBM Corp. 2007   *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>   */ @@ -11,7 +9,7 @@  #include <linux/init.h>  #include <linux/errno.h>  #include <linux/cpu.h> -#include <linux/sysdev.h> +#include <linux/device.h>  #include <linux/workqueue.h>  #include <asm/smp.h> @@ -31,13 +29,14 @@ static struct work_struct sclp_cpu_change_work;  static void sclp_cpu_capability_notify(struct work_struct *work)  {  	int cpu; -	struct sys_device *sysdev; +	struct device *dev; -	pr_warning("cpu capability changed.\n"); +	s390_adjust_jiffies(); +	pr_info("CPU capability may have changed\n");  	get_online_cpus();  	for_each_online_cpu(cpu) { -		sysdev = get_cpu_sysdev(cpu); -		kobject_uevent(&sysdev->kobj, KOBJ_CHANGE); +		dev = get_cpu_device(cpu); +		kobject_uevent(&dev->kobj, KOBJ_CHANGE);  	}  	put_online_cpus();  } @@ -70,21 +69,9 @@ static struct sclp_register sclp_conf_register =  static int __init sclp_conf_init(void)  { -	int rc; -  	INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);  	INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); - -	rc = sclp_register(&sclp_conf_register); -	if (rc) -		return rc; - -	if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) { -		pr_warning("no configuration management.\n"); -		sclp_unregister(&sclp_conf_register); -		rc = -ENOSYS; -	} -	return rc; +	return sclp_register(&sclp_conf_register);  }  __initcall(sclp_conf_init); diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 5716487b8c9..d70d8c20229 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/sclp_cpi.c   *    SCLP control programm identification   *   *    Copyright IBM Corp. 2001, 2007 diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c index 4a51e3f0968..2acea809e2a 100644 --- a/drivers/s390/char/sclp_cpi_sys.c +++ b/drivers/s390/char/sclp_cpi_sys.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/sclp_cpi_sys.c   *    SCLP control program identification sysfs interface   *   *    Copyright IBM Corp. 2001, 2007 @@ -21,6 +20,7 @@  #include <linux/err.h>  #include <linux/slab.h>  #include <linux/completion.h> +#include <linux/export.h>  #include <asm/ebcdic.h>  #include <asm/sclp.h> diff --git a/drivers/s390/char/sclp_cpi_sys.h b/drivers/s390/char/sclp_cpi_sys.h index deef3e6ff49..65bb6a99c97 100644 --- a/drivers/s390/char/sclp_cpi_sys.h +++ b/drivers/s390/char/sclp_cpi_sys.h @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/sclp_cpi_sys.h   *    SCLP control program identification sysfs interface   *   *    Copyright IBM Corp. 2007 diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c new file mode 100644 index 00000000000..648cb86afd4 --- /dev/null +++ b/drivers/s390/char/sclp_ctl.c @@ -0,0 +1,144 @@ +/* + * IOCTL interface for SCLP + * + * Copyright IBM Corp. 2012 + * + * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <linux/compat.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <asm/compat.h> +#include <asm/sclp_ctl.h> +#include <asm/sclp.h> + +#include "sclp.h" + +/* + * Supported command words + */ +static unsigned int sclp_ctl_sccb_wlist[] = { +	0x00400002, +	0x00410002, +}; + +/* + * Check if command word is supported + */ +static int sclp_ctl_cmdw_supported(unsigned int cmdw) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { +		if (cmdw == sclp_ctl_sccb_wlist[i]) +			return 1; +	} +	return 0; +} + +static void __user *u64_to_uptr(u64 value) +{ +	if (is_compat_task()) +		return compat_ptr(value); +	else +		return (void __user *)(unsigned long)value; +} + +/* + * Start SCLP request + */ +static int sclp_ctl_ioctl_sccb(void __user *user_area) +{ +	struct sclp_ctl_sccb ctl_sccb; +	struct sccb_header *sccb; +	int rc; + +	if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) +		return -EFAULT; +	if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) +		return -EOPNOTSUPP; +	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); +	if (!sccb) +		return -ENOMEM; +	if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { +		rc = -EFAULT; +		goto out_free; +	} +	if (sccb->length > PAGE_SIZE || sccb->length < 8) +		return -EINVAL; +	if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { +		rc = -EFAULT; +		goto out_free; +	} +	rc = sclp_sync_request(ctl_sccb.cmdw, sccb); +	if (rc) +		goto out_free; +	if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) +		rc = -EFAULT; +out_free: +	free_page((unsigned long) sccb); +	return rc; +} + +/* + * SCLP SCCB ioctl function + */ +static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, +			   unsigned long arg) +{ +	void __user *argp; + +	if (is_compat_task()) +		argp = compat_ptr(arg); +	else +		argp = (void __user *) arg; +	switch (cmd) { +	case SCLP_CTL_SCCB: +		return sclp_ctl_ioctl_sccb(argp); +	default: /* unknown ioctl number */ +		return -ENOTTY; +	} +} + +/* + * File operations + */ +static const struct file_operations sclp_ctl_fops = { +	.owner = THIS_MODULE, +	.open = nonseekable_open, +	.unlocked_ioctl = sclp_ctl_ioctl, +	.compat_ioctl = sclp_ctl_ioctl, +	.llseek = no_llseek, +}; + +/* + * Misc device definition + */ +static struct miscdevice sclp_ctl_device = { +	.minor = MISC_DYNAMIC_MINOR, +	.name = "sclp", +	.fops = &sclp_ctl_fops, +}; + +/* + * Register sclp_ctl misc device + */ +static int __init sclp_ctl_init(void) +{ +	return misc_register(&sclp_ctl_device); +} +module_init(sclp_ctl_init); + +/* + * Deregister sclp_ctl misc device + */ +static void __exit sclp_ctl_exit(void) +{ +	misc_deregister(&sclp_ctl_device); +} +module_exit(sclp_ctl_exit); diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c new file mode 100644 index 00000000000..1918d9dff45 --- /dev/null +++ b/drivers/s390/char/sclp_early.c @@ -0,0 +1,315 @@ +/* + * SCLP early driver + * + * Copyright IBM Corp. 2013 + */ + +#define KMSG_COMPONENT "sclp_early" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <asm/ctl_reg.h> +#include <asm/sclp.h> +#include <asm/ipl.h> +#include "sclp_sdias.h" +#include "sclp.h" + +#define SCLP_CMDW_READ_SCP_INFO		0x00020001 +#define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001 + +struct read_info_sccb { +	struct	sccb_header header;	/* 0-7 */ +	u16	rnmax;			/* 8-9 */ +	u8	rnsize;			/* 10 */ +	u8	_reserved0[16 - 11];	/* 11-15 */ +	u16	ncpurl;			/* 16-17 */ +	u16	cpuoff;			/* 18-19 */ +	u8	_reserved7[24 - 20];	/* 20-23 */ +	u8	loadparm[8];		/* 24-31 */ +	u8	_reserved1[48 - 32];	/* 32-47 */ +	u64	facilities;		/* 48-55 */ +	u8	_reserved2a[76 - 56];	/* 56-75 */ +	u32	ibc;			/* 76-79 */ +	u8	_reserved2b[84 - 80];	/* 80-83 */ +	u8	fac84;			/* 84 */ +	u8	fac85;			/* 85 */ +	u8	_reserved3[91 - 86];	/* 86-90 */ +	u8	flags;			/* 91 */ +	u8	_reserved4[100 - 92];	/* 92-99 */ +	u32	rnsize2;		/* 100-103 */ +	u64	rnmax2;			/* 104-111 */ +	u8	_reserved5[120 - 112];	/* 112-119 */ +	u16	hcpua;			/* 120-121 */ +	u8	_reserved6[4096 - 122];	/* 122-4095 */ +} __packed __aligned(PAGE_SIZE); + +static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; +static unsigned int sclp_con_has_vt220 __initdata; +static unsigned int sclp_con_has_linemode __initdata; +static unsigned long sclp_hsa_size; +static unsigned int sclp_max_cpu; +static struct sclp_ipl_info sclp_ipl_info; +static unsigned char sclp_siif; +static u32 sclp_ibc; + +u64 sclp_facilities; +u8 sclp_fac84; +unsigned long long sclp_rzm; +unsigned long long sclp_rnmax; + +static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) +{ +	int rc; + +	__ctl_set_bit(0, 9); +	rc = sclp_service_call(cmd, sccb); +	if (rc) +		goto out; +	__load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA | +			PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT); +	local_irq_disable(); +out: +	/* Contents of the sccb might have changed. */ +	barrier(); +	__ctl_clear_bit(0, 9); +	return rc; +} + +static int __init sclp_read_info_early(struct read_info_sccb *sccb) +{ +	int rc, i; +	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, +				  SCLP_CMDW_READ_SCP_INFO}; + +	for (i = 0; i < ARRAY_SIZE(commands); i++) { +		do { +			memset(sccb, 0, sizeof(*sccb)); +			sccb->header.length = sizeof(*sccb); +			sccb->header.function_code = 0x80; +			sccb->header.control_mask[2] = 0x80; +			rc = sclp_cmd_sync_early(commands[i], sccb); +		} while (rc == -EBUSY); + +		if (rc) +			break; +		if (sccb->header.response_code == 0x10) +			return 0; +		if (sccb->header.response_code != 0x1f0) +			break; +	} +	return -EIO; +} + +static void __init sclp_facilities_detect(struct read_info_sccb *sccb) +{ +	struct sclp_cpu_entry *cpue; +	u16 boot_cpu_address, cpu; + +	if (sclp_read_info_early(sccb)) +		return; + +	sclp_facilities = sccb->facilities; +	sclp_fac84 = sccb->fac84; +	if (sccb->fac85 & 0x02) +		S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; +	sclp_rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; +	sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; +	sclp_rzm <<= 20; +	sclp_ibc = sccb->ibc; + +	if (!sccb->hcpua) { +		if (MACHINE_IS_VM) +			sclp_max_cpu = 64; +		else +			sclp_max_cpu = sccb->ncpurl; +	} else { +		sclp_max_cpu = sccb->hcpua + 1; +	} + +	boot_cpu_address = stap(); +	cpue = (void *)sccb + sccb->cpuoff; +	for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) { +		if (boot_cpu_address != cpue->address) +			continue; +		sclp_siif = cpue->siif; +		break; +	} + +	/* Save IPL information */ +	sclp_ipl_info.is_valid = 1; +	if (sccb->flags & 0x2) +		sclp_ipl_info.has_dump = 1; +	memcpy(&sclp_ipl_info.loadparm, &sccb->loadparm, LOADPARM_LEN); +} + +bool __init sclp_has_linemode(void) +{ +	return !!sclp_con_has_linemode; +} + +bool __init sclp_has_vt220(void) +{ +	return !!sclp_con_has_vt220; +} + +unsigned long long sclp_get_rnmax(void) +{ +	return sclp_rnmax; +} + +unsigned long long sclp_get_rzm(void) +{ +	return sclp_rzm; +} + +unsigned int sclp_get_max_cpu(void) +{ +	return sclp_max_cpu; +} + +int sclp_has_siif(void) +{ +	return sclp_siif; +} +EXPORT_SYMBOL(sclp_has_siif); + +unsigned int sclp_get_ibc(void) +{ +	return sclp_ibc; +} +EXPORT_SYMBOL(sclp_get_ibc); + +/* + * This function will be called after sclp_facilities_detect(), which gets + * called from early.c code. The sclp_facilities_detect() function retrieves + * and saves the IPL information. + */ +void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +{ +	*info = sclp_ipl_info; +} + +static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb) +{ +	int rc; + +	do { +		rc = sclp_cmd_sync_early(cmd, sccb); +	} while (rc == -EBUSY); + +	if (rc) +		return -EIO; +	if (((struct sccb_header *) sccb)->response_code != 0x0020) +		return -EIO; +	return 0; +} + +static void __init sccb_init_eq_size(struct sdias_sccb *sccb) +{ +	memset(sccb, 0, sizeof(*sccb)); + +	sccb->hdr.length = sizeof(*sccb); +	sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); +	sccb->evbuf.hdr.type = EVTYP_SDIAS; +	sccb->evbuf.event_qual = SDIAS_EQ_SIZE; +	sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; +	sccb->evbuf.event_id = 4712; +	sccb->evbuf.dbs = 1; +} + +static int __init sclp_set_event_mask(struct init_sccb *sccb, +				      unsigned long receive_mask, +				      unsigned long send_mask) +{ +	memset(sccb, 0, sizeof(*sccb)); +	sccb->header.length = sizeof(*sccb); +	sccb->mask_length = sizeof(sccb_mask_t); +	sccb->receive_mask = receive_mask; +	sccb->send_mask = send_mask; +	return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb); +} + +static long __init sclp_hsa_size_init(struct sdias_sccb *sccb) +{ +	sccb_init_eq_size(sccb); +	if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) +		return -EIO; +	if (sccb->evbuf.blk_cnt == 0) +		return 0; +	return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; +} + +static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) +{ +	memset(sccb, 0, PAGE_SIZE); +	sccb->length = PAGE_SIZE; +	if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb)) +		return -EIO; +	if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0) +		return 0; +	return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE; +} + +unsigned long sclp_get_hsa_size(void) +{ +	return sclp_hsa_size; +} + +static void __init sclp_hsa_size_detect(void *sccb) +{ +	long size; + +	/* First try synchronous interface (LPAR) */ +	if (sclp_set_event_mask(sccb, 0, 0x40000010)) +		return; +	size = sclp_hsa_size_init(sccb); +	if (size < 0) +		return; +	if (size != 0) +		goto out; +	/* Then try asynchronous interface (z/VM) */ +	if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010)) +		return; +	size = sclp_hsa_size_init(sccb); +	if (size < 0) +		return; +	size = sclp_hsa_copy_wait(sccb); +	if (size < 0) +		return; +out: +	sclp_hsa_size = size; +} + +static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) +{ +	if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK))) +		return 0; +	if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) +		return 0; +	return 1; +} + +static void __init sclp_console_detect(struct init_sccb *sccb) +{ +	if (sccb->header.response_code != 0x20) +		return; + +	if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK) +		sclp_con_has_vt220 = 1; + +	if (sclp_con_check_linemode(sccb)) +		sclp_con_has_linemode = 1; +} + +void __init sclp_early_detect(void) +{ +	void *sccb = &sccb_early; + +	sclp_facilities_detect(sccb); +	sclp_hsa_size_detect(sccb); + +	/* Turn off SCLP event notifications.  Also save remote masks in the +	 * sccb.  These are sufficient to detect sclp console capabilities. +	 */ +	sclp_set_event_mask(sccb, 0, 0); +	sclp_console_detect(sccb); +} diff --git a/drivers/s390/char/sclp_ocf.c b/drivers/s390/char/sclp_ocf.c new file mode 100644 index 00000000000..2553db0fdb5 --- /dev/null +++ b/drivers/s390/char/sclp_ocf.c @@ -0,0 +1,144 @@ +/* + *    SCLP OCF communication parameters sysfs interface + * + *    Copyright IBM Corp. 2011 + *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> + */ + +#define KMSG_COMPONENT "sclp_ocf" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/stat.h> +#include <linux/device.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kmod.h> +#include <linux/timer.h> +#include <linux/err.h> +#include <asm/ebcdic.h> +#include <asm/sclp.h> + +#include "sclp.h" + +#define OCF_LENGTH_HMC_NETWORK 8UL +#define OCF_LENGTH_CPC_NAME 8UL + +static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1]; +static char cpc_name[OCF_LENGTH_CPC_NAME + 1]; + +static DEFINE_SPINLOCK(sclp_ocf_lock); +static struct work_struct sclp_ocf_change_work; + +static struct kset *ocf_kset; + +static void sclp_ocf_change_notify(struct work_struct *work) +{ +	kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE); +} + +/* Handler for OCF event. Look for the CPC image name. */ +static void sclp_ocf_handler(struct evbuf_header *evbuf) +{ +	struct gds_vector *v; +	struct gds_subvector *sv, *netid, *cpc; +	size_t size; + +	/* Find the 0x9f00 block. */ +	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, +				 0x9f00); +	if (!v) +		return; +	/* Find the 0x9f22 block inside the 0x9f00 block. */ +	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22); +	if (!v) +		return; +	/* Find the 0x81 block inside the 0x9f22 block. */ +	sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81); +	if (!sv) +		return; +	/* Find the 0x01 block inside the 0x81 block. */ +	netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1); +	/* Find the 0x02 block inside the 0x81 block. */ +	cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2); +	/* Copy network name and cpc name. */ +	spin_lock(&sclp_ocf_lock); +	if (netid) { +		size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length); +		memcpy(hmc_network, netid + 1, size); +		EBCASC(hmc_network, size); +		hmc_network[size] = 0; +	} +	if (cpc) { +		size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length); +		memcpy(cpc_name, cpc + 1, size); +		EBCASC(cpc_name, size); +		cpc_name[size] = 0; +	} +	spin_unlock(&sclp_ocf_lock); +	schedule_work(&sclp_ocf_change_work); +} + +static struct sclp_register sclp_ocf_event = { +	.receive_mask = EVTYP_OCF_MASK, +	.receiver_fn = sclp_ocf_handler, +}; + +static ssize_t cpc_name_show(struct kobject *kobj, +			     struct kobj_attribute *attr, char *page) +{ +	int rc; + +	spin_lock_irq(&sclp_ocf_lock); +	rc = snprintf(page, PAGE_SIZE, "%s\n", cpc_name); +	spin_unlock_irq(&sclp_ocf_lock); +	return rc; +} + +static struct kobj_attribute cpc_name_attr = +	__ATTR(cpc_name, 0444, cpc_name_show, NULL); + +static ssize_t hmc_network_show(struct kobject *kobj, +				struct kobj_attribute *attr, char *page) +{ +	int rc; + +	spin_lock_irq(&sclp_ocf_lock); +	rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network); +	spin_unlock_irq(&sclp_ocf_lock); +	return rc; +} + +static struct kobj_attribute hmc_network_attr = +	__ATTR(hmc_network, 0444, hmc_network_show, NULL); + +static struct attribute *ocf_attrs[] = { +	&cpc_name_attr.attr, +	&hmc_network_attr.attr, +	NULL, +}; + +static struct attribute_group ocf_attr_group = { +	.attrs = ocf_attrs, +}; + +static int __init ocf_init(void) +{ +	int rc; + +	INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify); +	ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj); +	if (!ocf_kset) +		return -ENOMEM; + +	rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group); +	if (rc) { +		kset_unregister(ocf_kset); +		return rc; +	} + +	return sclp_register(&sclp_ocf_event); +} + +device_initcall(ocf_init); diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index 05909a7df8b..475e470d976 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/sclp_quiesce.c   *     signal quiesce handler   * - *  (C) Copyright IBM Corp. 1999,2004 + *  Copyright IBM Corp. 1999, 2004   *  Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>   *             Peter Oberparleiter <peter.oberparleiter@de.ibm.com>   */ @@ -13,9 +12,8 @@  #include <linux/smp.h>  #include <linux/init.h>  #include <linux/reboot.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/ptrace.h> -#include <asm/sigp.h>  #include <asm/smp.h>  #include "sclp.h" @@ -30,7 +28,8 @@ static void do_machine_quiesce(void)  	psw_t quiesce_psw;  	smp_send_stop(); -	quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT; +	quiesce_psw.mask = +		PSW_MASK_BASE | PSW_MASK_EA | PSW_MASK_BA | PSW_MASK_WAIT;  	quiesce_psw.addr = 0xfff;  	__load_psw(quiesce_psw);  } diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 4be63be7344..3b13d58fe87 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -463,7 +463,7 @@ sclp_emit_buffer(struct sclp_buffer *buffer,  		/* Use write priority message */  		sccb->msg_buf.header.type = EVTYP_PMSGCMD;  	else -		return -ENOSYS; +		return -EOPNOTSUPP;  	buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;  	buffer->request.status = SCLP_REQ_FILLED;  	buffer->request.callback = sclp_writedata_callback; diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c index 6a1c58dc61a..561a0414b35 100644 --- a/drivers/s390/char/sclp_sdias.c +++ b/drivers/s390/char/sclp_sdias.c @@ -1,18 +1,20 @@  /* - * Sclp "store data in absolut storage" + * SCLP "store data in absolute storage"   * - * Copyright IBM Corp. 2003,2007 + * Copyright IBM Corp. 2003, 2013   * Author(s): Michael Holzheu   */  #define KMSG_COMPONENT "sclp_sdias"  #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include <linux/completion.h>  #include <linux/sched.h>  #include <asm/sclp.h>  #include <asm/debug.h>  #include <asm/ipl.h> +#include "sclp_sdias.h"  #include "sclp.h"  #include "sclp_rw.h" @@ -21,59 +23,36 @@  #define SDIAS_RETRIES 300  #define SDIAS_SLEEP_TICKS 50 -#define EQ_STORE_DATA	0x0 -#define EQ_SIZE		0x1 -#define DI_FCP_DUMP	0x0 -#define ASA_SIZE_32	0x0 -#define ASA_SIZE_64	0x1 -#define EVSTATE_ALL_STORED	0x0 -#define EVSTATE_NO_DATA		0x3 -#define EVSTATE_PART_STORED	0x10 -  static struct debug_info *sdias_dbf;  static struct sclp_register sclp_sdias_register = {  	.send_mask = EVTYP_SDIAS_MASK,  }; -struct sdias_evbuf { -	struct	evbuf_header hdr; -	u8	event_qual; -	u8	data_id; -	u64	reserved2; -	u32	event_id; -	u16	reserved3; -	u8	asa_size; -	u8	event_status; -	u32	reserved4; -	u32	blk_cnt; -	u64	asa; -	u32	reserved5; -	u32	fbn; -	u32	reserved6; -	u32	lbn; -	u16	reserved7; -	u16	dbs; -} __attribute__((packed)); - -struct sdias_sccb { -	struct sccb_header  hdr; -	struct sdias_evbuf  evbuf; -} __attribute__((packed)); -  static struct sdias_sccb sccb __attribute__((aligned(4096))); +static struct sdias_evbuf sdias_evbuf; -static int sclp_req_done; -static wait_queue_head_t sdias_wq; +static DECLARE_COMPLETION(evbuf_accepted); +static DECLARE_COMPLETION(evbuf_done);  static DEFINE_MUTEX(sdias_mutex); -static void sdias_callback(struct sclp_req *request, void *data) +/* + * Called by SCLP base when read event data has been completed (async mode only) + */ +static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf)  { -	struct sdias_sccb *cbsccb; +	memcpy(&sdias_evbuf, evbuf, +	       min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length)); +	complete(&evbuf_done); +	TRACE("sclp_sdias_receiver_fn done\n"); +} -	cbsccb = (struct sdias_sccb *) request->sccb; -	sclp_req_done = 1; -	wake_up(&sdias_wq); /* Inform caller, that request is complete */ +/* + * Called by SCLP base when sdias event has been accepted + */ +static void sdias_callback(struct sclp_req *request, void *data) +{ +	complete(&evbuf_accepted);  	TRACE("callback done\n");  } @@ -83,7 +62,6 @@ static int sdias_sclp_send(struct sclp_req *req)  	int rc;  	for (retries = SDIAS_RETRIES; retries; retries--) { -		sclp_req_done = 0;  		TRACE("add request\n");  		rc = sclp_add_request(req);  		if (rc) { @@ -94,16 +72,31 @@ static int sdias_sclp_send(struct sclp_req *req)  			continue;  		}  		/* initiated, wait for completion of service call */ -		wait_event(sdias_wq, (sclp_req_done == 1)); +		wait_for_completion(&evbuf_accepted);  		if (req->status == SCLP_REQ_FAILED) {  			TRACE("sclp request failed\n"); -			rc = -EIO;  			continue;  		} +		/* if not accepted, retry */ +		if (!(sccb.evbuf.hdr.flags & 0x80)) { +			TRACE("sclp request failed: flags=%x\n", +			      sccb.evbuf.hdr.flags); +			continue; +		} +		/* +		 * for the sync interface the response is in the initial sccb +		 */ +		if (!sclp_sdias_register.receiver_fn) { +			memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf)); +			TRACE("sync request done\n"); +			return 0; +		} +		/* otherwise we wait for completion */ +		wait_for_completion(&evbuf_done);  		TRACE("request done\n"); -		break; +		return 0;  	} -	return rc; +	return -EIO;  }  /* @@ -122,8 +115,8 @@ int sclp_sdias_blk_count(void)  	sccb.hdr.length = sizeof(sccb);  	sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);  	sccb.evbuf.hdr.type = EVTYP_SDIAS; -	sccb.evbuf.event_qual = EQ_SIZE; -	sccb.evbuf.data_id = DI_FCP_DUMP; +	sccb.evbuf.event_qual = SDIAS_EQ_SIZE; +	sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;  	sccb.evbuf.event_id = 4712;  	sccb.evbuf.dbs = 1; @@ -143,13 +136,12 @@ int sclp_sdias_blk_count(void)  		goto out;  	} -	switch (sccb.evbuf.event_status) { +	switch (sdias_evbuf.event_status) {  		case 0: -			rc = sccb.evbuf.blk_cnt; +			rc = sdias_evbuf.blk_cnt;  			break;  		default: -			pr_err("SCLP error: %x\n", -			       sccb.evbuf.event_status); +			pr_err("SCLP error: %x\n", sdias_evbuf.event_status);  			rc = -EIO;  			goto out;  	} @@ -183,13 +175,13 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)  	sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);  	sccb.evbuf.hdr.type = EVTYP_SDIAS;  	sccb.evbuf.hdr.flags = 0; -	sccb.evbuf.event_qual = EQ_STORE_DATA; -	sccb.evbuf.data_id = DI_FCP_DUMP; +	sccb.evbuf.event_qual = SDIAS_EQ_STORE_DATA; +	sccb.evbuf.data_id = SDIAS_DI_FCP_DUMP;  	sccb.evbuf.event_id = 4712; -#ifdef __s390x__ -	sccb.evbuf.asa_size = ASA_SIZE_64; +#ifdef CONFIG_64BIT +	sccb.evbuf.asa_size = SDIAS_ASA_SIZE_64;  #else -	sccb.evbuf.asa_size = ASA_SIZE_32; +	sccb.evbuf.asa_size = SDIAS_ASA_SIZE_32;  #endif  	sccb.evbuf.event_status = 0;  	sccb.evbuf.blk_cnt = nr_blks; @@ -214,38 +206,70 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)  		goto out;  	} -	switch (sccb.evbuf.event_status) { -		case EVSTATE_ALL_STORED: -			TRACE("all stored\n"); -		case EVSTATE_PART_STORED: -			TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); -			break; -		case EVSTATE_NO_DATA: -			TRACE("no data\n"); -		default: -			pr_err("Error from SCLP while copying hsa. " -			       "Event status = %x\n", -			       sccb.evbuf.event_status); -			rc = -EIO; +	switch (sdias_evbuf.event_status) { +	case SDIAS_EVSTATE_ALL_STORED: +		TRACE("all stored\n"); +		break; +	case SDIAS_EVSTATE_PART_STORED: +		TRACE("part stored: %i\n", sdias_evbuf.blk_cnt); +		break; +	case SDIAS_EVSTATE_NO_DATA: +		TRACE("no data\n"); +		/* fall through */ +	default: +		pr_err("Error from SCLP while copying hsa. Event status = %x\n", +		       sdias_evbuf.event_status); +		rc = -EIO;  	}  out:  	mutex_unlock(&sdias_mutex);  	return rc;  } -int __init sclp_sdias_init(void) +static int __init sclp_sdias_register_check(void)  {  	int rc; +	rc = sclp_register(&sclp_sdias_register); +	if (rc) +		return rc; +	if (sclp_sdias_blk_count() == 0) { +		sclp_unregister(&sclp_sdias_register); +		return -ENODEV; +	} +	return 0; +} + +static int __init sclp_sdias_init_sync(void) +{ +	TRACE("Try synchronous mode\n"); +	sclp_sdias_register.receive_mask = 0; +	sclp_sdias_register.receiver_fn = NULL; +	return sclp_sdias_register_check(); +} + +static int __init sclp_sdias_init_async(void) +{ +	TRACE("Try asynchronous mode\n"); +	sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK; +	sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn; +	return sclp_sdias_register_check(); +} + +int __init sclp_sdias_init(void) +{  	if (ipl_info.type != IPL_TYPE_FCP_DUMP)  		return 0;  	sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));  	debug_register_view(sdias_dbf, &debug_sprintf_view);  	debug_set_level(sdias_dbf, 6); -	rc = sclp_register(&sclp_sdias_register); -	if (rc) -		return rc; -	init_waitqueue_head(&sdias_wq); +	if (sclp_sdias_init_sync() == 0) +		goto out; +	if (sclp_sdias_init_async() == 0) +		goto out; +	TRACE("init failed\n"); +	return -ENODEV; +out:  	TRACE("init done\n");  	return 0;  } diff --git a/drivers/s390/char/sclp_sdias.h b/drivers/s390/char/sclp_sdias.h new file mode 100644 index 00000000000..f2431c41415 --- /dev/null +++ b/drivers/s390/char/sclp_sdias.h @@ -0,0 +1,46 @@ +/* + * SCLP "store data in absolute storage" + * + * Copyright IBM Corp. 2003, 2013 + */ + +#ifndef SCLP_SDIAS_H +#define SCLP_SDIAS_H + +#include "sclp.h" + +#define SDIAS_EQ_STORE_DATA		0x0 +#define SDIAS_EQ_SIZE			0x1 +#define SDIAS_DI_FCP_DUMP		0x0 +#define SDIAS_ASA_SIZE_32		0x0 +#define SDIAS_ASA_SIZE_64		0x1 +#define SDIAS_EVSTATE_ALL_STORED	0x0 +#define SDIAS_EVSTATE_NO_DATA		0x3 +#define SDIAS_EVSTATE_PART_STORED	0x10 + +struct sdias_evbuf { +	struct	evbuf_header hdr; +	u8	event_qual; +	u8	data_id; +	u64	reserved2; +	u32	event_id; +	u16	reserved3; +	u8	asa_size; +	u8	event_status; +	u32	reserved4; +	u32	blk_cnt; +	u64	asa; +	u32	reserved5; +	u32	fbn; +	u32	reserved6; +	u32	lbn; +	u16	reserved7; +	u16	dbs; +} __packed; + +struct sdias_sccb { +	struct sccb_header	hdr; +	struct sdias_evbuf	evbuf; +} __packed; + +#endif /* SCLP_SDIAS_H */ diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 8258d590505..7ed7a598781 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/sclp_tty.c   *    SCLP line mode terminal driver.   *   *  S390 version - *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 1999   *    Author(s): Martin Peschke <mpeschke@de.ibm.com>   *		 Martin Schwidefsky <schwidefsky@de.ibm.com>   */ @@ -48,7 +47,7 @@ static struct sclp_buffer *sclp_ttybuf;  /* Timer for delayed output of console messages. */  static struct timer_list sclp_tty_timer; -static struct tty_struct *sclp_tty; +static struct tty_port sclp_port;  static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];  static unsigned short int sclp_tty_chars_count; @@ -64,9 +63,9 @@ static int sclp_tty_columns = 80;  static int  sclp_tty_open(struct tty_struct *tty, struct file *filp)  { -	sclp_tty = tty; +	tty_port_tty_set(&sclp_port, tty);  	tty->driver_data = NULL; -	tty->low_latency = 0; +	sclp_port.low_latency = 0;  	return 0;  } @@ -76,7 +75,7 @@ sclp_tty_close(struct tty_struct *tty, struct file *filp)  {  	if (tty->count > 1)  		return; -	sclp_tty = NULL; +	tty_port_tty_set(&sclp_port, NULL);  }  /* @@ -125,10 +124,8 @@ sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)  					    struct sclp_buffer, list);  		spin_unlock_irqrestore(&sclp_tty_lock, flags);  	} while (buffer && sclp_emit_buffer(buffer, sclp_ttybuf_callback)); -	/* check if the tty needs a wake up call */ -	if (sclp_tty != NULL) { -		tty_wakeup(sclp_tty); -	} + +	tty_port_tty_wakeup(&sclp_port);  }  static inline void @@ -326,21 +323,22 @@ sclp_tty_flush_buffer(struct tty_struct *tty)  static void  sclp_tty_input(unsigned char* buf, unsigned int count)  { +	struct tty_struct *tty = tty_port_tty_get(&sclp_port);  	unsigned int cchar;  	/*  	 * If this tty driver is currently closed  	 * then throw the received input away.  	 */ -	if (sclp_tty == NULL) +	if (tty == NULL)  		return; -	cchar = ctrlchar_handle(buf, count, sclp_tty); +	cchar = ctrlchar_handle(buf, count, tty);  	switch (cchar & CTRLCHAR_MASK) {  	case CTRLCHAR_SYSRQ:  		break;  	case CTRLCHAR_CTRL: -		tty_insert_flip_char(sclp_tty, cchar, TTY_NORMAL); -		tty_flip_buffer_push(sclp_tty); +		tty_insert_flip_char(&sclp_port, cchar, TTY_NORMAL); +		tty_flip_buffer_push(&sclp_port);  		break;  	case CTRLCHAR_NONE:  		/* send (normal) input to line discipline */ @@ -348,13 +346,14 @@ sclp_tty_input(unsigned char* buf, unsigned int count)  		    (strncmp((const char *) buf + count - 2, "^n", 2) &&  		     strncmp((const char *) buf + count - 2, "\252n", 2))) {  			/* add the auto \n */ -			tty_insert_flip_string(sclp_tty, buf, count); -			tty_insert_flip_char(sclp_tty, '\n', TTY_NORMAL); +			tty_insert_flip_string(&sclp_port, buf, count); +			tty_insert_flip_char(&sclp_port, '\n', TTY_NORMAL);  		} else -			tty_insert_flip_string(sclp_tty, buf, count - 2); -		tty_flip_buffer_push(sclp_tty); +			tty_insert_flip_string(&sclp_port, buf, count - 2); +		tty_flip_buffer_push(&sclp_port);  		break;  	} +	tty_kref_put(tty);  }  /* @@ -408,118 +407,72 @@ static int sclp_switch_cases(unsigned char *buf, int count)  	return op - buf;  } -static void -sclp_get_input(unsigned char *start, unsigned char *end) +static void sclp_get_input(struct gds_subvector *sv)  { +	unsigned char *str;  	int count; -	count = end - start; +	str = (unsigned char *) (sv + 1); +	count = sv->length - sizeof(*sv);  	if (sclp_tty_tolower) -		EBC_TOLOWER(start, count); -	count = sclp_switch_cases(start, count); +		EBC_TOLOWER(str, count); +	count = sclp_switch_cases(str, count);  	/* convert EBCDIC to ASCII (modify original input in SCCB) */ -	sclp_ebcasc_str(start, count); +	sclp_ebcasc_str(str, count);  	/* transfer input to high level driver */ -	sclp_tty_input(start, count); +	sclp_tty_input(str, count);  } -static inline struct gds_vector * -find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id) +static inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv)  { -	struct gds_vector *vec; +	void *end; -	for (vec = start; vec < end; vec = (void *) vec + vec->length) -		if (vec->gds_id == id) -			return vec; -	return NULL; +	end = (void *) sv + sv->length; +	for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length) +		if (sv->key == 0x30) +			sclp_get_input(sv);  } -static inline struct gds_subvector * -find_gds_subvector(struct gds_subvector *start, -		   struct gds_subvector *end, u8 key) +static inline void sclp_eval_textcmd(struct gds_vector *v)  { -	struct gds_subvector *subvec; +	struct gds_subvector *sv; +	void *end; -	for (subvec = start; subvec < end; -	     subvec = (void *) subvec + subvec->length) -		if (subvec->key == key) -			return subvec; -	return NULL; -} - -static inline void -sclp_eval_selfdeftextmsg(struct gds_subvector *start, -			 struct gds_subvector *end) -{ -	struct gds_subvector *subvec; +	end = (void *) v + v->length; +	for (sv = (struct gds_subvector *) (v + 1); +	     (void *) sv < end; sv = (void *) sv + sv->length) +		if (sv->key == GDS_KEY_SELFDEFTEXTMSG) +			sclp_eval_selfdeftextmsg(sv); -	subvec = start; -	while (subvec < end) { -		subvec = find_gds_subvector(subvec, end, 0x30); -		if (!subvec) -			break; -		sclp_get_input((unsigned char *)(subvec + 1), -			       (unsigned char *) subvec + subvec->length); -		subvec = (void *) subvec + subvec->length; -	}  } -static inline void -sclp_eval_textcmd(struct gds_subvector *start, -		  struct gds_subvector *end) +static inline void sclp_eval_cpmsu(struct gds_vector *v)  { -	struct gds_subvector *subvec; +	void *end; -	subvec = start; -	while (subvec < end) { -		subvec = find_gds_subvector(subvec, end, -					    GDS_KEY_SELFDEFTEXTMSG); -		if (!subvec) -			break; -		sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), -					 (void *)subvec + subvec->length); -		subvec = (void *) subvec + subvec->length; -	} +	end = (void *) v + v->length; +	for (v = v + 1; (void *) v < end; v = (void *) v + v->length) +		if (v->gds_id == GDS_ID_TEXTCMD) +			sclp_eval_textcmd(v);  } -static inline void -sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) -{ -	struct gds_vector *vec; - -	vec = start; -	while (vec < end) { -		vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD); -		if (!vec) -			break; -		sclp_eval_textcmd((struct gds_subvector *)(vec + 1), -				  (void *) vec + vec->length); -		vec = (void *) vec + vec->length; -	} -} - -static inline void -sclp_eval_mdsmu(struct gds_vector *start, void *end) +static inline void sclp_eval_mdsmu(struct gds_vector *v)  { -	struct gds_vector *vec; - -	vec = find_gds_vector(start, end, GDS_ID_CPMSU); -	if (vec) -		sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length); +	v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU); +	if (v) +		sclp_eval_cpmsu(v);  } -static void -sclp_tty_receiver(struct evbuf_header *evbuf) +static void sclp_tty_receiver(struct evbuf_header *evbuf)  { -	struct gds_vector *start, *end, *vec; +	struct gds_vector *v; -	start = (struct gds_vector *)(evbuf + 1); -	end = (void *) evbuf + evbuf->length; -	vec = find_gds_vector(start, end, GDS_ID_MDSMU); -	if (vec) -		sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length); +	v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length, +				 GDS_ID_MDSMU); +	if (v) +		sclp_eval_mdsmu(v);  }  static void @@ -589,7 +542,6 @@ sclp_tty_init(void)  		sclp_tty_tolower = 1;  	}  	sclp_tty_chars_count = 0; -	sclp_tty = NULL;  	rc = sclp_register(&sclp_input_event);  	if (rc) { @@ -597,7 +549,8 @@ sclp_tty_init(void)  		return rc;  	} -	driver->owner = THIS_MODULE; +	tty_port_init(&sclp_port); +  	driver->driver_name = "sclp_line";  	driver->name = "sclp_line";  	driver->major = TTY_MAJOR; @@ -610,9 +563,11 @@ sclp_tty_init(void)  	driver->init_termios.c_lflag = ISIG | ECHO;  	driver->flags = TTY_DRIVER_REAL_RAW;  	tty_set_operations(driver, &sclp_ops); +	tty_port_link_device(&sclp_port, driver, 0);  	rc = tty_register_driver(driver);  	if (rc) {  		put_tty_driver(driver); +		tty_port_destroy(&sclp_port);  		return rc;  	}  	sclp_tty_driver = driver; diff --git a/drivers/s390/char/sclp_tty.h b/drivers/s390/char/sclp_tty.h index 4b965b22fec..c8773421c31 100644 --- a/drivers/s390/char/sclp_tty.h +++ b/drivers/s390/char/sclp_tty.h @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/sclp_tty.h   *    interface to the SCLP-read/write driver   *   *  S390 version - *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 1999   *    Author(s): Martin Peschke <mpeschke@de.ibm.com>   *		 Martin Schwidefsky <schwidefsky@de.ibm.com>   */ diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 5d706e6c946..b9a9f721716 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -34,7 +34,6 @@  #define SCLP_VT220_DEVICE_NAME		"ttysclp"  #define SCLP_VT220_CONSOLE_NAME		"ttyS"  #define SCLP_VT220_CONSOLE_INDEX	1	/* console=ttyS1 */ -#define SCLP_VT220_BUF_SIZE		80  /* Representation of a single write request */  struct sclp_vt220_request { @@ -56,8 +55,7 @@ struct sclp_vt220_sccb {  /* Structures and data needed to register tty driver */  static struct tty_driver *sclp_vt220_driver; -/* The tty_struct that the kernel associated with us */ -static struct tty_struct *sclp_vt220_tty; +static struct tty_port sclp_vt220_port;  /* Lock to protect internal data from concurrent access */  static spinlock_t sclp_vt220_lock; @@ -99,13 +97,16 @@ static void sclp_vt220_pm_event_fn(struct sclp_register *reg,  static int __sclp_vt220_emit(struct sclp_vt220_request *request);  static void sclp_vt220_emit_current(void); -/* Registration structure for our interest in SCLP event buffers */ +/* Registration structure for SCLP output event buffers */  static struct sclp_register sclp_vt220_register = {  	.send_mask		= EVTYP_VT220MSG_MASK, +	.pm_event_fn		= sclp_vt220_pm_event_fn, +}; + +/* Registration structure for SCLP input event buffers */ +static struct sclp_register sclp_vt220_register_input = {  	.receive_mask		= EVTYP_VT220MSG_MASK, -	.state_change_fn	= NULL,  	.receiver_fn		= sclp_vt220_receiver_fn, -	.pm_event_fn		= sclp_vt220_pm_event_fn,  }; @@ -140,10 +141,7 @@ sclp_vt220_process_queue(struct sclp_vt220_request *request)  	} while (__sclp_vt220_emit(request));  	if (request == NULL && sclp_vt220_flush_later)  		sclp_vt220_emit_current(); -	/* Check if the tty needs a wake up call */ -	if (sclp_vt220_tty != NULL) { -		tty_wakeup(sclp_vt220_tty); -	} +	tty_port_tty_wakeup(&sclp_vt220_port);  }  #define SCLP_BUFFER_MAX_RETRY		1 @@ -367,6 +365,31 @@ sclp_vt220_timeout(unsigned long data)  #define BUFFER_MAX_DELAY	HZ/20 +/* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_vt220_drop_buffer(void) +{ +	struct list_head *list; +	struct sclp_vt220_request *request; +	void *page; + +	if (!sclp_console_drop) +		return 0; +	list = sclp_vt220_outqueue.next; +	if (sclp_vt220_queue_running) +		/* The first element is in I/O */ +		list = list->next; +	if (list == &sclp_vt220_outqueue) +		return 0; +	list_del(list); +	request = list_entry(list, struct sclp_vt220_request, list); +	page = request->sclp_req.sccb; +	list_add_tail((struct list_head *) page, &sclp_vt220_empty); +	return 1; +} +  /*    * Internal implementation of the write function. Write COUNT bytes of data   * from memory at BUF @@ -395,12 +418,16 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,  	do {  		/* Create an sclp output buffer if none exists yet */  		if (sclp_vt220_current_request == NULL) { +			if (list_empty(&sclp_vt220_empty)) +				sclp_console_full++;  			while (list_empty(&sclp_vt220_empty)) { -				spin_unlock_irqrestore(&sclp_vt220_lock, flags);  				if (may_fail || sclp_vt220_suspended)  					goto out; -				else -					sclp_sync_wait(); +				if (sclp_vt220_drop_buffer()) +					break; +				spin_unlock_irqrestore(&sclp_vt220_lock, flags); + +				sclp_sync_wait();  				spin_lock_irqsave(&sclp_vt220_lock, flags);  			}  			page = (void *) sclp_vt220_empty.next; @@ -433,8 +460,8 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule,  		sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY;  		add_timer(&sclp_vt220_timer);  	} -	spin_unlock_irqrestore(&sclp_vt220_lock, flags);  out: +	spin_unlock_irqrestore(&sclp_vt220_lock, flags);  	return overall_written;  } @@ -463,10 +490,6 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf)  	char *buffer;  	unsigned int count; -	/* Ignore input if device is not open */ -	if (sclp_vt220_tty == NULL) -		return; -  	buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));  	count = evbuf->length - sizeof(struct evbuf_header); @@ -478,8 +501,8 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf)  		/* Send input to line discipline */  		buffer++;  		count--; -		tty_insert_flip_string(sclp_vt220_tty, buffer, count); -		tty_flip_buffer_push(sclp_vt220_tty); +		tty_insert_flip_string(&sclp_vt220_port, buffer, count); +		tty_flip_buffer_push(&sclp_vt220_port);  		break;  	}  } @@ -491,11 +514,8 @@ static int  sclp_vt220_open(struct tty_struct *tty, struct file *filp)  {  	if (tty->count == 1) { -		sclp_vt220_tty = tty; -		tty->driver_data = kmalloc(SCLP_VT220_BUF_SIZE, GFP_KERNEL); -		if (tty->driver_data == NULL) -			return -ENOMEM; -		tty->low_latency = 0; +		tty_port_tty_set(&sclp_vt220_port, tty); +		sclp_vt220_port.low_latency = 0;  		if (!tty->winsize.ws_row && !tty->winsize.ws_col) {  			tty->winsize.ws_row = 24;  			tty->winsize.ws_col = 80; @@ -510,11 +530,8 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp)  static void  sclp_vt220_close(struct tty_struct *tty, struct file *filp)  { -	if (tty->count == 1) { -		sclp_vt220_tty = NULL; -		kfree(tty->driver_data); -		tty->driver_data = NULL; -	} +	if (tty->count == 1) +		tty_port_tty_set(&sclp_vt220_port, NULL);  }  /* @@ -618,6 +635,7 @@ static void __init __sclp_vt220_cleanup(void)  		return;  	sclp_unregister(&sclp_vt220_register);  	__sclp_vt220_free_pages(); +	tty_port_destroy(&sclp_vt220_port);  }  /* Allocate buffer pages and register with sclp core. Controlled by init @@ -635,9 +653,9 @@ static int __init __sclp_vt220_init(int num_pages)  	INIT_LIST_HEAD(&sclp_vt220_empty);  	INIT_LIST_HEAD(&sclp_vt220_outqueue);  	init_timer(&sclp_vt220_timer); +	tty_port_init(&sclp_vt220_port);  	sclp_vt220_current_request = NULL;  	sclp_vt220_buffered_chars = 0; -	sclp_vt220_tty = NULL;  	sclp_vt220_flush_later = 0;  	/* Allocate pages for output buffering */ @@ -653,6 +671,7 @@ out:  	if (rc) {  		__sclp_vt220_free_pages();  		sclp_vt220_init_count--; +		tty_port_destroy(&sclp_vt220_port);  	}  	return rc;  } @@ -685,7 +704,6 @@ static int __init sclp_vt220_tty_init(void)  	if (rc)  		goto out_driver; -	driver->owner = THIS_MODULE;  	driver->driver_name = SCLP_VT220_DRIVER_NAME;  	driver->name = SCLP_VT220_DEVICE_NAME;  	driver->major = SCLP_VT220_MAJOR; @@ -695,13 +713,19 @@ static int __init sclp_vt220_tty_init(void)  	driver->init_termios = tty_std_termios;  	driver->flags = TTY_DRIVER_REAL_RAW;  	tty_set_operations(driver, &sclp_vt220_ops); +	tty_port_link_device(&sclp_vt220_port, driver, 0);  	rc = tty_register_driver(driver);  	if (rc)  		goto out_init; +	rc = sclp_register(&sclp_vt220_register_input); +	if (rc) +		goto out_reg;  	sclp_vt220_driver = driver;  	return 0; +out_reg: +	tty_unregister_driver(driver);  out_init:  	__sclp_vt220_cleanup();  out_driver: @@ -814,9 +838,7 @@ sclp_vt220_con_init(void)  {  	int rc; -	if (!CONSOLE_IS_SCLP) -		return 0; -	rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); +	rc = __sclp_vt220_init(sclp_console_pages);  	if (rc)  		return rc;  	/* Attach linux console */ diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h index 7a242f07363..ea664dd4f56 100644 --- a/drivers/s390/char/tape.h +++ b/drivers/s390/char/tape.h @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/tape.h   *    tape device driver for 3480/3490E/3590 tapes.   *   *  S390 and zSeries version @@ -16,7 +15,6 @@  #include <asm/ccwdev.h>  #include <asm/debug.h>  #include <asm/idals.h> -#include <linux/blkdev.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/mtio.h> @@ -154,12 +152,6 @@ struct tape_discipline {  	struct tape_request *(*read_block)(struct tape_device *, size_t);  	struct tape_request *(*write_block)(struct tape_device *, size_t);  	void (*process_eov)(struct tape_device*); -#ifdef CONFIG_S390_TAPE_BLOCK -	/* Block device stuff. */ -	struct tape_request *(*bread)(struct tape_device *, struct request *); -	void (*check_locate)(struct tape_device *, struct tape_request *); -	void (*free_bread)(struct tape_request *); -#endif  	/* ioctl function for additional ioctls. */  	int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long);  	/* Array of tape commands with TAPE_NR_MTOPS entries */ @@ -182,26 +174,6 @@ struct tape_char_data {  	int block_size;			/*   of size block_size. */  }; -#ifdef CONFIG_S390_TAPE_BLOCK -/* Block Frontend Data */ -struct tape_blk_data -{ -	struct tape_device *	device; -	/* Block device request queue. */ -	struct request_queue *	request_queue; -	spinlock_t		request_queue_lock; - -	/* Task to move entries from block request to CCS request queue. */ -	struct work_struct	requeue_task; -	atomic_t		requeue_scheduled; - -	/* Current position on the tape. */ -	long			block_position; -	int			medium_changed; -	struct gendisk *	disk; -}; -#endif -  /* Tape Info */  struct tape_device {  	/* entry in tape_device_list */ @@ -248,10 +220,6 @@ struct tape_device {  	/* Character device frontend data */  	struct tape_char_data		char_data; -#ifdef CONFIG_S390_TAPE_BLOCK -	/* Block dev frontend data */ -	struct tape_blk_data		blk_data; -#endif  	/* Function to start or stop the next request later. */  	struct delayed_work		tape_dnr; @@ -280,6 +248,14 @@ tape_do_io_free(struct tape_device *device, struct tape_request *request)  	return rc;  } +static inline void +tape_do_io_async_free(struct tape_device *device, struct tape_request *request) +{ +	request->callback = (void *) tape_free_request; +	request->callback_data = NULL; +	tape_do_io_async(device, request); +} +  extern int tape_oper_handler(int irq, int status);  extern void tape_noper_handler(int irq, int status);  extern int tape_open(struct tape_device *); @@ -305,19 +281,6 @@ extern void tapechar_exit(void);  extern int  tapechar_setup_device(struct tape_device *);  extern void tapechar_cleanup_device(struct tape_device *); -/* Externals from tape_block.c */ -#ifdef CONFIG_S390_TAPE_BLOCK -extern int tapeblock_init (void); -extern void tapeblock_exit(void); -extern int tapeblock_setup_device(struct tape_device *); -extern void tapeblock_cleanup_device(struct tape_device *); -#else -static inline int tapeblock_init (void) {return 0;} -static inline void tapeblock_exit (void) {;} -static inline int tapeblock_setup_device(struct tape_device *t) {return 0;} -static inline void tapeblock_cleanup_device (struct tape_device *t) {;} -#endif -  /* tape initialisation functions */  #ifdef CONFIG_PROC_FS  extern void tape_proc_init (void); diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index c17f35b6136..9aa79702b37 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/tape_34xx.c   *    tape device discipline for 3480/3490 tapes.   *   *    Copyright IBM Corp. 2001, 2009 @@ -53,23 +52,11 @@ static void tape_34xx_delete_sbid_from(struct tape_device *, int);   * Medium sense for 34xx tapes. There is no 'real' medium sense call.   * So we just do a normal sense.   */ -static int -tape_34xx_medium_sense(struct tape_device *device) +static void __tape_34xx_medium_sense(struct tape_request *request)  { -	struct tape_request *request; -	unsigned char       *sense; -	int                  rc; - -	request = tape_alloc_request(1, 32); -	if (IS_ERR(request)) { -		DBF_EXCEPTION(6, "MSEN fail\n"); -		return PTR_ERR(request); -	} - -	request->op = TO_MSEN; -	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); +	struct tape_device *device = request->device; +	unsigned char *sense; -	rc = tape_do_io_interruptible(device, request);  	if (request->rc == 0) {  		sense = request->cpdata; @@ -88,15 +75,47 @@ tape_34xx_medium_sense(struct tape_device *device)  			device->tape_generic_status |= GMT_WR_PROT(~0);  		else  			device->tape_generic_status &= ~GMT_WR_PROT(~0); -	} else { +	} else  		DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n",  			request->rc); -	}  	tape_free_request(request); +} + +static int tape_34xx_medium_sense(struct tape_device *device) +{ +	struct tape_request *request; +	int rc; + +	request = tape_alloc_request(1, 32); +	if (IS_ERR(request)) { +		DBF_EXCEPTION(6, "MSEN fail\n"); +		return PTR_ERR(request); +	} +	request->op = TO_MSEN; +	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); +	rc = tape_do_io_interruptible(device, request); +	__tape_34xx_medium_sense(request);  	return rc;  } +static void tape_34xx_medium_sense_async(struct tape_device *device) +{ +	struct tape_request *request; + +	request = tape_alloc_request(1, 32); +	if (IS_ERR(request)) { +		DBF_EXCEPTION(6, "MSEN fail\n"); +		return; +	} + +	request->op = TO_MSEN; +	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata); +	request->callback = (void *) __tape_34xx_medium_sense; +	request->callback_data = NULL; +	tape_do_io_async(device, request); +} +  struct tape_34xx_work {  	struct tape_device	*device;  	enum tape_op		 op; @@ -109,6 +128,9 @@ struct tape_34xx_work {   * is inserted but cannot call tape_do_io* from an interrupt context.   * Maybe that's useful for other actions we want to start from the   * interrupt handler. + * Note: the work handler is called by the system work queue. The tape + * commands started by the handler need to be asynchrounous, otherwise + * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).   */  static void  tape_34xx_work_handler(struct work_struct *work) @@ -119,7 +141,7 @@ tape_34xx_work_handler(struct work_struct *work)  	switch(p->op) {  		case TO_MSEN: -			tape_34xx_medium_sense(device); +			tape_34xx_medium_sense_async(device);  			break;  		default:  			DBF_EVENT(3, "T34XX: internal error: unknown work\n"); @@ -300,20 +322,6 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request,  	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;  	sense = irb->ecw; -#ifdef CONFIG_S390_TAPE_BLOCK -	if (request->op == TO_BLOCK) { -		/* -		 * Recovery for block device requests. Set the block_position -		 * to something invalid and retry. -		 */ -		device->blk_data.block_position = -1; -		if (request->retries-- <= 0) -			return tape_34xx_erp_failed(request, -EIO); -		else -			return tape_34xx_erp_retry(request); -	} -#endif -  	if (  		sense[0] & SENSE_COMMAND_REJECT &&  		sense[1] & SENSE_WRITE_PROTECT @@ -1106,123 +1114,6 @@ tape_34xx_mtseek(struct tape_device *device, int mt_count)  	return tape_do_io_free(device, request);  } -#ifdef CONFIG_S390_TAPE_BLOCK -/* - * Tape block read for 34xx. - */ -static struct tape_request * -tape_34xx_bread(struct tape_device *device, struct request *req) -{ -	struct tape_request *request; -	struct ccw1 *ccw; -	int count = 0; -	unsigned off; -	char *dst; -	struct bio_vec *bv; -	struct req_iterator iter; -	struct tape_34xx_block_id *	start_block; - -	DBF_EVENT(6, "xBREDid:"); - -	/* Count the number of blocks for the request. */ -	rq_for_each_segment(bv, req, iter) -		count += bv->bv_len >> (TAPEBLOCK_HSEC_S2B + 9); - -	/* Allocate the ccw request. */ -	request = tape_alloc_request(3+count+1, 8); -	if (IS_ERR(request)) -		return request; - -	/* Setup ccws. */ -	request->op = TO_BLOCK; -	start_block = (struct tape_34xx_block_id *) request->cpdata; -	start_block->block = blk_rq_pos(req) >> TAPEBLOCK_HSEC_S2B; -	DBF_EVENT(6, "start_block = %i\n", start_block->block); - -	ccw = request->cpaddr; -	ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte); - -	/* -	 * We always setup a nop after the mode set ccw. This slot is -	 * used in tape_std_check_locate to insert a locate ccw if the -	 * current tape position doesn't match the start block to be read. -	 * The second nop will be filled with a read block id which is in -	 * turn used by tape_34xx_free_bread to populate the segment bid -	 * table. -	 */ -	ccw = tape_ccw_cc(ccw, NOP, 0, NULL); -	ccw = tape_ccw_cc(ccw, NOP, 0, NULL); - -	rq_for_each_segment(bv, req, iter) { -		dst = kmap(bv->bv_page) + bv->bv_offset; -		for (off = 0; off < bv->bv_len; off += TAPEBLOCK_HSEC_SIZE) { -			ccw->flags = CCW_FLAG_CC; -			ccw->cmd_code = READ_FORWARD; -			ccw->count = TAPEBLOCK_HSEC_SIZE; -			set_normalized_cda(ccw, (void*) __pa(dst)); -			ccw++; -			dst += TAPEBLOCK_HSEC_SIZE; -		} -	} - -	ccw = tape_ccw_end(ccw, NOP, 0, NULL); -	DBF_EVENT(6, "xBREDccwg\n"); -	return request; -} - -static void -tape_34xx_free_bread (struct tape_request *request) -{ -	struct ccw1* ccw; - -	ccw = request->cpaddr; -	if ((ccw + 2)->cmd_code == READ_BLOCK_ID) { -		struct { -			struct tape_34xx_block_id	cbid; -			struct tape_34xx_block_id	dbid; -		} __attribute__ ((packed)) *rbi_data; - -		rbi_data = request->cpdata; - -		if (request->device) -			tape_34xx_add_sbid(request->device, rbi_data->cbid); -	} - -	/* Last ccw is a nop and doesn't need clear_normalized_cda */ -	for (; ccw->flags & CCW_FLAG_CC; ccw++) -		if (ccw->cmd_code == READ_FORWARD) -			clear_normalized_cda(ccw); -	tape_free_request(request); -} - -/* - * check_locate is called just before the tape request is passed to - * the common io layer for execution. It has to check the current - * tape position and insert a locate ccw if it doesn't match the - * start block for the request. - */ -static void -tape_34xx_check_locate(struct tape_device *device, struct tape_request *request) -{ -	struct tape_34xx_block_id *	start_block; - -	start_block = (struct tape_34xx_block_id *) request->cpdata; -	if (start_block->block == device->blk_data.block_position) -		return; - -	DBF_LH(4, "Block seek(%06d+%06d)\n", start_block->block, device->bof); -	start_block->wrap    = 0; -	start_block->segment = 1; -	start_block->format  = (*device->modeset_byte & 0x08) ? -				TAPE34XX_FMT_3480_XF : -				TAPE34XX_FMT_3480; -	start_block->block   = start_block->block + device->bof; -	tape_34xx_merge_sbid(device, start_block); -	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); -	tape_ccw_cc(request->cpaddr + 2, READ_BLOCK_ID, 8, request->cpdata); -} -#endif -  /*   * List of 3480/3490 magnetic tape commands.   */ @@ -1272,11 +1163,6 @@ static struct tape_discipline tape_discipline_34xx = {  	.irq = tape_34xx_irq,  	.read_block = tape_std_read_block,  	.write_block = tape_std_write_block, -#ifdef CONFIG_S390_TAPE_BLOCK -	.bread = tape_34xx_bread, -	.free_bread = tape_34xx_free_bread, -	.check_locate = tape_34xx_check_locate, -#endif  	.ioctl_fn = tape_34xx_ioctl,  	.mtop_array = tape_34xx_mtop  }; @@ -1297,14 +1183,17 @@ tape_34xx_online(struct ccw_device *cdev)  }  static struct ccw_driver tape_34xx_driver = { -	.name = "tape_34xx", -	.owner = THIS_MODULE, +	.driver = { +		.name = "tape_34xx", +		.owner = THIS_MODULE, +	},  	.ids = tape_34xx_ids,  	.probe = tape_generic_probe,  	.remove = tape_generic_remove,  	.set_online = tape_34xx_online,  	.set_offline = tape_generic_offline,  	.freeze = tape_generic_pm_suspend, +	.int_class = IRQIO_TAP,  };  static int diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index deff2c3361e..327cb19ad0b 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/tape_3590.c   *    tape device discipline for 3590 tapes.   *   *    Copyright IBM Corp. 2001, 2009 @@ -24,6 +23,8 @@  #include "tape_std.h"  #include "tape_3590.h" +static struct workqueue_struct *tape_3590_wq; +  /*   * Pointer to debug area.   */ @@ -327,17 +328,17 @@ out:  /*   * Enable encryption   */ -static int tape_3592_enable_crypt(struct tape_device *device) +static struct tape_request *__tape_3592_enable_crypt(struct tape_device *device)  {  	struct tape_request *request;  	char *data;  	DBF_EVENT(6, "tape_3592_enable_crypt\n");  	if (!crypt_supported(device)) -		return -ENOSYS; +		return ERR_PTR(-ENOSYS);  	request = tape_alloc_request(2, 72);  	if (IS_ERR(request)) -		return PTR_ERR(request); +		return request;  	data = request->cpdata;  	memset(data,0,72); @@ -352,23 +353,42 @@ static int tape_3592_enable_crypt(struct tape_device *device)  	request->op = TO_CRYPT_ON;  	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);  	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); +	return request; +} + +static int tape_3592_enable_crypt(struct tape_device *device) +{ +	struct tape_request *request; + +	request = __tape_3592_enable_crypt(device); +	if (IS_ERR(request)) +		return PTR_ERR(request);  	return tape_do_io_free(device, request);  } +static void tape_3592_enable_crypt_async(struct tape_device *device) +{ +	struct tape_request *request; + +	request = __tape_3592_enable_crypt(device); +	if (!IS_ERR(request)) +		tape_do_io_async_free(device, request); +} +  /*   * Disable encryption   */ -static int tape_3592_disable_crypt(struct tape_device *device) +static struct tape_request *__tape_3592_disable_crypt(struct tape_device *device)  {  	struct tape_request *request;  	char *data;  	DBF_EVENT(6, "tape_3592_disable_crypt\n");  	if (!crypt_supported(device)) -		return -ENOSYS; +		return ERR_PTR(-ENOSYS);  	request = tape_alloc_request(2, 72);  	if (IS_ERR(request)) -		return PTR_ERR(request); +		return request;  	data = request->cpdata;  	memset(data,0,72); @@ -381,9 +401,28 @@ static int tape_3592_disable_crypt(struct tape_device *device)  	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);  	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36); +	return request; +} + +static int tape_3592_disable_crypt(struct tape_device *device) +{ +	struct tape_request *request; + +	request = __tape_3592_disable_crypt(device); +	if (IS_ERR(request)) +		return PTR_ERR(request);  	return tape_do_io_free(device, request);  } +static void tape_3592_disable_crypt_async(struct tape_device *device) +{ +	struct tape_request *request; + +	request = __tape_3592_disable_crypt(device); +	if (!IS_ERR(request)) +		tape_do_io_async_free(device, request); +} +  /*   * IOCTL: Set encryption status   */ @@ -455,8 +494,7 @@ tape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)  /*   * SENSE Medium: Get Sense data about medium state   */ -static int -tape_3590_sense_medium(struct tape_device *device) +static int tape_3590_sense_medium(struct tape_device *device)  {  	struct tape_request *request; @@ -468,6 +506,18 @@ tape_3590_sense_medium(struct tape_device *device)  	return tape_do_io_free(device, request);  } +static void tape_3590_sense_medium_async(struct tape_device *device) +{ +	struct tape_request *request; + +	request = tape_alloc_request(1, 128); +	if (IS_ERR(request)) +		return; +	request->op = TO_MSEN; +	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata); +	tape_do_io_async_free(device, request); +} +  /*   * MTTELL: Tell block. Return the number of block relative to current file.   */ @@ -544,15 +594,14 @@ tape_3590_read_opposite(struct tape_device *device,   * 2. The attention msg is written to the "read subsystem data" buffer.   * In this case we probably should print it to the console.   */ -static int -tape_3590_read_attmsg(struct tape_device *device) +static void tape_3590_read_attmsg_async(struct tape_device *device)  {  	struct tape_request *request;  	char *buf;  	request = tape_alloc_request(3, 4096);  	if (IS_ERR(request)) -		return PTR_ERR(request); +		return;  	request->op = TO_READ_ATTMSG;  	buf = request->cpdata;  	buf[0] = PREP_RD_SS_DATA; @@ -560,12 +609,15 @@ tape_3590_read_attmsg(struct tape_device *device)  	tape_ccw_cc(request->cpaddr, PERFORM_SS_FUNC, 12, buf);  	tape_ccw_cc(request->cpaddr + 1, READ_SS_DATA, 4096 - 12, buf + 12);  	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); -	return tape_do_io_free(device, request); +	tape_do_io_async_free(device, request);  }  /*   * These functions are used to schedule follow-up actions from within an   * interrupt context (like unsolicited interrupts). + * Note: the work handler is called by the system work queue. The tape + * commands started by the handler need to be asynchrounous, otherwise + * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).   */  struct work_handler_data {  	struct tape_device *device; @@ -581,16 +633,16 @@ tape_3590_work_handler(struct work_struct *work)  	switch (p->op) {  	case TO_MSEN: -		tape_3590_sense_medium(p->device); +		tape_3590_sense_medium_async(p->device);  		break;  	case TO_READ_ATTMSG: -		tape_3590_read_attmsg(p->device); +		tape_3590_read_attmsg_async(p->device);  		break;  	case TO_CRYPT_ON: -		tape_3592_enable_crypt(p->device); +		tape_3592_enable_crypt_async(p->device);  		break;  	case TO_CRYPT_OFF: -		tape_3592_disable_crypt(p->device); +		tape_3592_disable_crypt_async(p->device);  		break;  	default:  		DBF_EVENT(3, "T3590: work handler undefined for " @@ -613,96 +665,10 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)  	p->device = tape_get_device(device);  	p->op = op; -	schedule_work(&p->work); +	queue_work(tape_3590_wq, &p->work);  	return 0;  } -#ifdef CONFIG_S390_TAPE_BLOCK -/* - * Tape Block READ - */ -static struct tape_request * -tape_3590_bread(struct tape_device *device, struct request *req) -{ -	struct tape_request *request; -	struct ccw1 *ccw; -	int count = 0, start_block; -	unsigned off; -	char *dst; -	struct bio_vec *bv; -	struct req_iterator iter; - -	DBF_EVENT(6, "xBREDid:"); -	start_block = blk_rq_pos(req) >> TAPEBLOCK_HSEC_S2B; -	DBF_EVENT(6, "start_block = %i\n", start_block); - -	rq_for_each_segment(bv, req, iter) -		count += bv->bv_len >> (TAPEBLOCK_HSEC_S2B + 9); - -	request = tape_alloc_request(2 + count + 1, 4); -	if (IS_ERR(request)) -		return request; -	request->op = TO_BLOCK; -	*(__u32 *) request->cpdata = start_block; -	ccw = request->cpaddr; -	ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte); - -	/* -	 * We always setup a nop after the mode set ccw. This slot is -	 * used in tape_std_check_locate to insert a locate ccw if the -	 * current tape position doesn't match the start block to be read. -	 */ -	ccw = tape_ccw_cc(ccw, NOP, 0, NULL); - -	rq_for_each_segment(bv, req, iter) { -		dst = page_address(bv->bv_page) + bv->bv_offset; -		for (off = 0; off < bv->bv_len; off += TAPEBLOCK_HSEC_SIZE) { -			ccw->flags = CCW_FLAG_CC; -			ccw->cmd_code = READ_FORWARD; -			ccw->count = TAPEBLOCK_HSEC_SIZE; -			set_normalized_cda(ccw, (void *) __pa(dst)); -			ccw++; -			dst += TAPEBLOCK_HSEC_SIZE; -		} -		BUG_ON(off > bv->bv_len); -	} -	ccw = tape_ccw_end(ccw, NOP, 0, NULL); -	DBF_EVENT(6, "xBREDccwg\n"); -	return request; -} - -static void -tape_3590_free_bread(struct tape_request *request) -{ -	struct ccw1 *ccw; - -	/* Last ccw is a nop and doesn't need clear_normalized_cda */ -	for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++) -		if (ccw->cmd_code == READ_FORWARD) -			clear_normalized_cda(ccw); -	tape_free_request(request); -} - -/* - * check_locate is called just before the tape request is passed to - * the common io layer for execution. It has to check the current - * tape position and insert a locate ccw if it doesn't match the - * start block for the request. - */ -static void -tape_3590_check_locate(struct tape_device *device, struct tape_request *request) -{ -	__u32 *start_block; - -	start_block = (__u32 *) request->cpdata; -	if (*start_block != device->blk_data.block_position) { -		/* Add the start offset of the file to get the real block. */ -		*start_block += device->bof; -		tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata); -	} -} -#endif -  static void tape_3590_med_state_set(struct tape_device *device,  				    struct tape_3590_med_sense *sense)  { @@ -743,10 +709,8 @@ static void tape_3590_med_state_set(struct tape_device *device,  static int  tape_3590_done(struct tape_device *device, struct tape_request *request)  { -	struct tape_3590_disc_data *disc_data;  	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); -	disc_data = device->discdata;  	switch (request->op) {  	case TO_BSB: @@ -1341,17 +1305,12 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)  static int tape_3590_crypt_error(struct tape_device *device,  				 struct tape_request *request, struct irb *irb)  { -	u8 cu_rc, ekm_rc1; +	u8 cu_rc;  	u16 ekm_rc2; -	u32 drv_rc; -	const char *bus_id;  	char *sense;  	sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data; -	bus_id = dev_name(&device->cdev->dev);  	cu_rc = sense[0]; -	drv_rc = *((u32*) &sense[5]) & 0xffffff; -	ekm_rc1 = sense[9];  	ekm_rc2 = *((u16*) &sense[10]);  	if ((cu_rc == 0) && (ekm_rc2 == 0xee31))  		/* key not defined on EKM */ @@ -1376,21 +1335,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,  		     struct irb *irb)  {  	struct tape_3590_sense *sense; -	int rc; - -#ifdef CONFIG_S390_TAPE_BLOCK -	if (request->op == TO_BLOCK) { -		/* -		 * Recovery for block device requests. Set the block_position -		 * to something invalid and retry. -		 */ -		device->blk_data.block_position = -1; -		if (request->retries-- <= 0) -			return tape_3590_erp_failed(device, request, irb, -EIO); -		else -			return tape_3590_erp_retry(device, request, irb); -	} -#endif  	sense = (struct tape_3590_sense *) irb->ecw; @@ -1401,7 +1345,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,  	 *   - "break":     basic error recovery is done  	 *   - "goto out:": just print error message if available  	 */ -	rc = -EIO;  	switch (sense->rc_rqc) {  	case 0x1110: @@ -1629,7 +1572,7 @@ fail_kmalloc:  static void  tape_3590_cleanup_device(struct tape_device *device)  { -	flush_scheduled_work(); +	flush_workqueue(tape_3590_wq);  	tape_std_unassign(device);  	kfree(device->discdata); @@ -1685,11 +1628,6 @@ static struct tape_discipline tape_discipline_3590 = {  	.irq = tape_3590_irq,  	.read_block = tape_std_read_block,  	.write_block = tape_std_write_block, -#ifdef CONFIG_S390_TAPE_BLOCK -	.bread = tape_3590_bread, -	.free_bread = tape_3590_free_bread, -	.check_locate = tape_3590_check_locate, -#endif  	.ioctl_fn = tape_3590_ioctl,  	.mtop_array = tape_3590_mtop  }; @@ -1708,14 +1646,17 @@ tape_3590_online(struct ccw_device *cdev)  }  static struct ccw_driver tape_3590_driver = { -	.name = "tape_3590", -	.owner = THIS_MODULE, +	.driver = { +		.name = "tape_3590", +		.owner = THIS_MODULE, +	},  	.ids = tape_3590_ids,  	.probe = tape_generic_probe,  	.remove = tape_generic_remove,  	.set_offline = tape_generic_offline,  	.set_online = tape_3590_online,  	.freeze = tape_generic_pm_suspend, +	.int_class = IRQIO_TAP,  };  /* @@ -1733,11 +1674,17 @@ tape_3590_init(void)  #endif  	DBF_EVENT(3, "3590 init\n"); + +	tape_3590_wq = alloc_workqueue("tape_3590", 0, 0); +	if (!tape_3590_wq) +		return -ENOMEM; +  	/* Register driver for 3590 tapes. */  	rc = ccw_driver_register(&tape_3590_driver); -	if (rc) +	if (rc) { +		destroy_workqueue(tape_3590_wq);  		DBF_EVENT(3, "3590 init failed\n"); -	else +	} else  		DBF_EVENT(3, "3590 registered\n");  	return rc;  } @@ -1746,7 +1693,7 @@ static void  tape_3590_exit(void)  {  	ccw_driver_unregister(&tape_3590_driver); - +	destroy_workqueue(tape_3590_wq);  	debug_unregister(TAPE_DBF_AREA);  } diff --git a/drivers/s390/char/tape_3590.h b/drivers/s390/char/tape_3590.h index 4534055f137..36b759e89d2 100644 --- a/drivers/s390/char/tape_3590.h +++ b/drivers/s390/char/tape_3590.h @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/tape_3590.h   *    tape device discipline for 3590 tapes.   * - *    Copyright IBM Corp. 2001,2006 + *    Copyright IBM Corp. 2001, 2006   *    Author(s): Stefan Bader <shbader@de.ibm.com>   *		 Michael Holzheu <holzheu@de.ibm.com>   *		 Martin Schwidefsky <schwidefsky@de.ibm.com> diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c deleted file mode 100644 index f0fa9ca5cb2..00000000000 --- a/drivers/s390/char/tape_block.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - *  drivers/s390/char/tape_block.c - *    block device frontend for tape device driver - * - *  S390 and zSeries version - *    Copyright (C) 2001,2003 IBM Deutschland Entwicklung GmbH, IBM Corporation - *    Author(s): Carsten Otte <cotte@de.ibm.com> - *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> - *		 Martin Schwidefsky <schwidefsky@de.ibm.com> - *		 Stefan Bader <shbader@de.ibm.com> - */ - -#define KMSG_COMPONENT "tape" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#include <linux/fs.h> -#include <linux/module.h> -#include <linux/blkdev.h> -#include <linux/mutex.h> -#include <linux/interrupt.h> -#include <linux/buffer_head.h> -#include <linux/kernel.h> - -#include <asm/debug.h> - -#define TAPE_DBF_AREA	tape_core_dbf - -#include "tape.h" - -#define TAPEBLOCK_MAX_SEC	100 -#define TAPEBLOCK_MIN_REQUEUE	3 - -/* - * 2003/11/25  Stefan Bader <shbader@de.ibm.com> - * - * In 2.5/2.6 the block device request function is very likely to be called - * with disabled interrupts (e.g. generic_unplug_device). So the driver can't - * just call any function that tries to allocate CCW requests from that con- - * text since it might sleep. There are two choices to work around this: - *	a) do not allocate with kmalloc but use its own memory pool - *      b) take requests from the queue outside that context, knowing that - *         allocation might sleep - */ - -/* - * file operation structure for tape block frontend - */ -static DEFINE_MUTEX(tape_block_mutex); -static int tapeblock_open(struct block_device *, fmode_t); -static int tapeblock_release(struct gendisk *, fmode_t); -static int tapeblock_medium_changed(struct gendisk *); -static int tapeblock_revalidate_disk(struct gendisk *); - -static const struct block_device_operations tapeblock_fops = { -	.owner		 = THIS_MODULE, -	.open		 = tapeblock_open, -	.release	 = tapeblock_release, -	.media_changed   = tapeblock_medium_changed, -	.revalidate_disk = tapeblock_revalidate_disk, -}; - -static int tapeblock_major = 0; - -static void -tapeblock_trigger_requeue(struct tape_device *device) -{ -	/* Protect against rescheduling. */ -	if (atomic_cmpxchg(&device->blk_data.requeue_scheduled, 0, 1) != 0) -		return; -	schedule_work(&device->blk_data.requeue_task); -} - -/* - * Post finished request. - */ -static void -__tapeblock_end_request(struct tape_request *ccw_req, void *data) -{ -	struct tape_device *device; -	struct request *req; - -	DBF_LH(6, "__tapeblock_end_request()\n"); - -	device = ccw_req->device; -	req = (struct request *) data; -	blk_end_request_all(req, (ccw_req->rc == 0) ? 0 : -EIO); -	if (ccw_req->rc == 0) -		/* Update position. */ -		device->blk_data.block_position = -		  (blk_rq_pos(req) + blk_rq_sectors(req)) >> TAPEBLOCK_HSEC_S2B; -	else -		/* We lost the position information due to an error. */ -		device->blk_data.block_position = -1; -	device->discipline->free_bread(ccw_req); -	if (!list_empty(&device->req_queue) || -	    blk_peek_request(device->blk_data.request_queue)) -		tapeblock_trigger_requeue(device); -} - -/* - * Feed the tape device CCW queue with requests supplied in a list. - */ -static int -tapeblock_start_request(struct tape_device *device, struct request *req) -{ -	struct tape_request *	ccw_req; -	int			rc; - -	DBF_LH(6, "tapeblock_start_request(%p, %p)\n", device, req); - -	ccw_req = device->discipline->bread(device, req); -	if (IS_ERR(ccw_req)) { -		DBF_EVENT(1, "TBLOCK: bread failed\n"); -		blk_end_request_all(req, -EIO); -		return PTR_ERR(ccw_req); -	} -	ccw_req->callback = __tapeblock_end_request; -	ccw_req->callback_data = (void *) req; -	ccw_req->retries = TAPEBLOCK_RETRIES; - -	rc = tape_do_io_async(device, ccw_req); -	if (rc) { -		/* -		 * Start/enqueueing failed. No retries in -		 * this case. -		 */ -		blk_end_request_all(req, -EIO); -		device->discipline->free_bread(ccw_req); -	} - -	return rc; -} - -/* - * Move requests from the block device request queue to the tape device ccw - * queue. - */ -static void -tapeblock_requeue(struct work_struct *work) { -	struct tape_blk_data *	blkdat; -	struct tape_device *	device; -	struct request_queue *	queue; -	int			nr_queued; -	struct request *	req; -	struct list_head *	l; -	int			rc; - -	blkdat = container_of(work, struct tape_blk_data, requeue_task); -	device = blkdat->device; -	if (!device) -		return; - -	spin_lock_irq(get_ccwdev_lock(device->cdev)); -	queue  = device->blk_data.request_queue; - -	/* Count number of requests on ccw queue. */ -	nr_queued = 0; -	list_for_each(l, &device->req_queue) -		nr_queued++; -	spin_unlock(get_ccwdev_lock(device->cdev)); - -	spin_lock_irq(&device->blk_data.request_queue_lock); -	while ( -		!blk_queue_plugged(queue) && -		blk_peek_request(queue) && -		nr_queued < TAPEBLOCK_MIN_REQUEUE -	) { -		req = blk_fetch_request(queue); -		if (rq_data_dir(req) == WRITE) { -			DBF_EVENT(1, "TBLOCK: Rejecting write request\n"); -			spin_unlock_irq(&device->blk_data.request_queue_lock); -			blk_end_request_all(req, -EIO); -			spin_lock_irq(&device->blk_data.request_queue_lock); -			continue; -		} -		nr_queued++; -		spin_unlock_irq(&device->blk_data.request_queue_lock); -		rc = tapeblock_start_request(device, req); -		spin_lock_irq(&device->blk_data.request_queue_lock); -	} -	spin_unlock_irq(&device->blk_data.request_queue_lock); -	atomic_set(&device->blk_data.requeue_scheduled, 0); -} - -/* - * Tape request queue function. Called from ll_rw_blk.c - */ -static void -tapeblock_request_fn(struct request_queue *queue) -{ -	struct tape_device *device; - -	device = (struct tape_device *) queue->queuedata; -	DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device); -	BUG_ON(device == NULL); -	tapeblock_trigger_requeue(device); -} - -/* - * This function is called for every new tapedevice - */ -int -tapeblock_setup_device(struct tape_device * device) -{ -	struct tape_blk_data *	blkdat; -	struct gendisk *	disk; -	int			rc; - -	blkdat = &device->blk_data; -	blkdat->device = device; -	spin_lock_init(&blkdat->request_queue_lock); -	atomic_set(&blkdat->requeue_scheduled, 0); - -	blkdat->request_queue = blk_init_queue( -		tapeblock_request_fn, -		&blkdat->request_queue_lock -	); -	if (!blkdat->request_queue) -		return -ENOMEM; - -	rc = elevator_change(blkdat->request_queue, "noop"); -	if (rc) -		goto cleanup_queue; - -	blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE); -	blk_queue_max_hw_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC); -	blk_queue_max_segments(blkdat->request_queue, -1L); -	blk_queue_max_segment_size(blkdat->request_queue, -1L); -	blk_queue_segment_boundary(blkdat->request_queue, -1L); - -	disk = alloc_disk(1); -	if (!disk) { -		rc = -ENOMEM; -		goto cleanup_queue; -	} - -	disk->major = tapeblock_major; -	disk->first_minor = device->first_minor; -	disk->fops = &tapeblock_fops; -	disk->private_data = tape_get_device(device); -	disk->queue = blkdat->request_queue; -	set_capacity(disk, 0); -	sprintf(disk->disk_name, "btibm%d", -		device->first_minor / TAPE_MINORS_PER_DEV); - -	blkdat->disk = disk; -	blkdat->medium_changed = 1; -	blkdat->request_queue->queuedata = tape_get_device(device); - -	add_disk(disk); - -	tape_get_device(device); -	INIT_WORK(&blkdat->requeue_task, tapeblock_requeue); - -	return 0; - -cleanup_queue: -	blk_cleanup_queue(blkdat->request_queue); -	blkdat->request_queue = NULL; - -	return rc; -} - -void -tapeblock_cleanup_device(struct tape_device *device) -{ -	flush_scheduled_work(); -	tape_put_device(device); - -	if (!device->blk_data.disk) { -		goto cleanup_queue; -	} - -	del_gendisk(device->blk_data.disk); -	device->blk_data.disk->private_data = NULL; -	tape_put_device(device); -	put_disk(device->blk_data.disk); - -	device->blk_data.disk = NULL; -cleanup_queue: -	device->blk_data.request_queue->queuedata = NULL; -	tape_put_device(device); - -	blk_cleanup_queue(device->blk_data.request_queue); -	device->blk_data.request_queue = NULL; -} - -/* - * Detect number of blocks of the tape. - * FIXME: can we extent this to detect the blocks size as well ? - */ -static int -tapeblock_revalidate_disk(struct gendisk *disk) -{ -	struct tape_device *	device; -	unsigned int		nr_of_blks; -	int			rc; - -	device = (struct tape_device *) disk->private_data; -	BUG_ON(!device); - -	if (!device->blk_data.medium_changed) -		return 0; - -	rc = tape_mtop(device, MTFSFM, 1); -	if (rc) -		return rc; - -	rc = tape_mtop(device, MTTELL, 1); -	if (rc < 0) -		return rc; - -	pr_info("%s: Determining the size of the recorded area...\n", -		dev_name(&device->cdev->dev)); -	DBF_LH(3, "Image file ends at %d\n", rc); -	nr_of_blks = rc; - -	/* This will fail for the first file. Catch the error by checking the -	 * position. */ -	tape_mtop(device, MTBSF, 1); - -	rc = tape_mtop(device, MTTELL, 1); -	if (rc < 0) -		return rc; - -	if (rc > nr_of_blks) -		return -EINVAL; - -	DBF_LH(3, "Image file starts at %d\n", rc); -	device->bof = rc; -	nr_of_blks -= rc; - -	pr_info("%s: The size of the recorded area is %i blocks\n", -		dev_name(&device->cdev->dev), nr_of_blks); -	set_capacity(device->blk_data.disk, -		nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512)); - -	device->blk_data.block_position = 0; -	device->blk_data.medium_changed = 0; -	return 0; -} - -static int -tapeblock_medium_changed(struct gendisk *disk) -{ -	struct tape_device *device; - -	device = (struct tape_device *) disk->private_data; -	DBF_LH(6, "tapeblock_medium_changed(%p) = %d\n", -		device, device->blk_data.medium_changed); - -	return device->blk_data.medium_changed; -} - -/* - * Block frontend tape device open function. - */ -static int -tapeblock_open(struct block_device *bdev, fmode_t mode) -{ -	struct gendisk *	disk = bdev->bd_disk; -	struct tape_device *	device; -	int			rc; - -	mutex_lock(&tape_block_mutex); -	device = tape_get_device(disk->private_data); - -	if (device->required_tapemarks) { -		DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); -		pr_warning("%s: Opening the tape failed because of missing " -			   "end-of-file marks\n", dev_name(&device->cdev->dev)); -		rc = -EPERM; -		goto put_device; -	} - -	rc = tape_open(device); -	if (rc) -		goto put_device; - -	rc = tapeblock_revalidate_disk(disk); -	if (rc) -		goto release; - -	/* -	 * Note: The reference to <device> is hold until the release function -	 *       is called. -	 */ -	tape_state_set(device, TS_BLKUSE); -	mutex_unlock(&tape_block_mutex); -	return 0; - -release: -	tape_release(device); - put_device: -	tape_put_device(device); -	mutex_unlock(&tape_block_mutex); -	return rc; -} - -/* - * Block frontend tape device release function. - * - * Note: One reference to the tape device was made by the open function. So - *       we just get the pointer here and release the reference. - */ -static int -tapeblock_release(struct gendisk *disk, fmode_t mode) -{ -	struct tape_device *device = disk->private_data; -  -	mutex_lock(&tape_block_mutex); -	tape_state_set(device, TS_IN_USE); -	tape_release(device); -	tape_put_device(device); -	mutex_unlock(&tape_block_mutex); - -	return 0; -} - -/* - * Initialize block device frontend. - */ -int -tapeblock_init(void) -{ -	int rc; - -	/* Register the tape major number to the kernel */ -	rc = register_blkdev(tapeblock_major, "tBLK"); -	if (rc < 0) -		return rc; - -	if (tapeblock_major == 0) -		tapeblock_major = rc; -	return 0; -} - -/* - * Deregister major for block device frontend - */ -void -tapeblock_exit(void) -{ -	unregister_blkdev(tapeblock_major, "tBLK"); -} diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 883e2db02bd..6dc60725de9 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/tape_char.c   *    character device frontend for tape device driver   *   *  S390 and zSeries version - *    Copyright IBM Corp. 2001,2006 + *    Copyright IBM Corp. 2001, 2006   *    Author(s): Carsten Otte <cotte@de.ibm.com>   *		 Michael Holzheu <holzheu@de.ibm.com>   *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> @@ -17,7 +16,6 @@  #include <linux/types.h>  #include <linux/proc_fs.h>  #include <linux/mtio.h> -#include <linux/smp_lock.h>  #include <linux/compat.h>  #include <asm/uaccess.h> @@ -140,7 +138,7 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)  	/*  	 * If the tape isn't terminated yet, do it now. And since we then  	 * are at the end of the tape there wouldn't be anything to read -	 * anyways. So we return immediatly. +	 * anyways. So we return immediately.  	 */  	if(device->required_tapemarks) {  		return tape_std_terminate_write(device); @@ -162,11 +160,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)  	if (rc)  		return rc; -#ifdef CONFIG_S390_TAPE_BLOCK -	/* Changes position. */ -	device->blk_data.medium_changed = 1; -#endif -  	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);  	/* Let the discipline build the ccw chain. */  	request = device->discipline->read_block(device, block_size); @@ -219,11 +212,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t  	if (rc)  		return rc; -#ifdef CONFIG_S390_TAPE_BLOCK -	/* Changes position. */ -	device->blk_data.medium_changed = 1; -#endif -  	DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);  	DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);  	/* Let the discipline build the ccw chain. */ @@ -285,13 +273,13 @@ tapechar_open (struct inode *inode, struct file *filp)  	int minor, rc;  	DBF_EVENT(6, "TCHAR:open: %i:%i\n", -		imajor(filp->f_path.dentry->d_inode), -		iminor(filp->f_path.dentry->d_inode)); +		imajor(file_inode(filp)), +		iminor(file_inode(filp))); -	if (imajor(filp->f_path.dentry->d_inode) != tapechar_major) +	if (imajor(file_inode(filp)) != tapechar_major)  		return -ENODEV; -	minor = iminor(filp->f_path.dentry->d_inode); +	minor = iminor(file_inode(filp));  	device = tape_find_device(minor / TAPE_MINORS_PER_DEV);  	if (IS_ERR(device)) {  		DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n"); @@ -380,9 +368,6 @@ __tapechar_ioctl(struct tape_device *device,  			case MTBSFM:  			case MTFSFM:  			case MTSEEK: -#ifdef CONFIG_S390_TAPE_BLOCK -				device->blk_data.medium_changed = 1; -#endif  				if (device->required_tapemarks)  					tape_std_terminate_write(device);  			default: diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 55343df61ed..91c3c642c76 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -1,6 +1,5 @@  /* - * (C) Copyright IBM Corp. 2004 - * tape_class.c + * Copyright IBM Corp. 2004   *   * Tape class device support   * @@ -17,7 +16,7 @@  MODULE_AUTHOR("Stefan Bader <shbader@de.ibm.com>");  MODULE_DESCRIPTION( -	"(C) Copyright IBM Corp. 2004   All Rights Reserved.\n" +	"Copyright IBM Corp. 2004   All Rights Reserved.\n"  	"tape_class.c"  );  MODULE_LICENSE("GPL"); @@ -78,7 +77,7 @@ struct tape_class_device *register_tape_dev(  	tcd->class_device = device_create(tape_class, device,  					  tcd->char_device->dev, NULL,  					  "%s", tcd->device_name); -	rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0; +	rc = PTR_RET(tcd->class_device);  	if (rc)  		goto fail_with_cdev;  	rc = sysfs_create_link( diff --git a/drivers/s390/char/tape_class.h b/drivers/s390/char/tape_class.h index 707b7f48c23..a332c10d50a 100644 --- a/drivers/s390/char/tape_class.h +++ b/drivers/s390/char/tape_class.h @@ -1,6 +1,5 @@  /* - * (C) Copyright IBM Corp. 2004   All Rights Reserved. - * tape_class.h + * Copyright IBM Corp. 2004   All Rights Reserved.   *   * Tape class device support   * @@ -14,8 +13,6 @@  #include <linux/module.h>  #include <linux/fs.h>  #include <linux/major.h> -#include <linux/kobject.h> -#include <linux/kobj_map.h>  #include <linux/cdev.h>  #include <linux/device.h> diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 6c408670e08..f3b5123faf0 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -1,5 +1,4 @@  /* - *  drivers/s390/char/tape_core.c   *    basic function of the tape device driver   *   *  S390 and zSeries version @@ -209,29 +208,79 @@ tape_state_set(struct tape_device *device, enum tape_state newstate)  	wake_up(&device->state_change_wq);  } +struct tape_med_state_work_data { +	struct tape_device *device; +	enum tape_medium_state state; +	struct work_struct  work; +}; + +static void +tape_med_state_work_handler(struct work_struct *work) +{ +	static char env_state_loaded[] = "MEDIUM_STATE=LOADED"; +	static char env_state_unloaded[] = "MEDIUM_STATE=UNLOADED"; +	struct tape_med_state_work_data *p = +		container_of(work, struct tape_med_state_work_data, work); +	struct tape_device *device = p->device; +	char *envp[] = { NULL, NULL }; + +	switch (p->state) { +	case MS_UNLOADED: +		pr_info("%s: The tape cartridge has been successfully " +			"unloaded\n", dev_name(&device->cdev->dev)); +		envp[0] = env_state_unloaded; +		kobject_uevent_env(&device->cdev->dev.kobj, KOBJ_CHANGE, envp); +		break; +	case MS_LOADED: +		pr_info("%s: A tape cartridge has been mounted\n", +			dev_name(&device->cdev->dev)); +		envp[0] = env_state_loaded; +		kobject_uevent_env(&device->cdev->dev.kobj, KOBJ_CHANGE, envp); +		break; +	default: +		break; +	} +	tape_put_device(device); +	kfree(p); +} + +static void +tape_med_state_work(struct tape_device *device, enum tape_medium_state state) +{ +	struct tape_med_state_work_data *p; + +	p = kzalloc(sizeof(*p), GFP_ATOMIC); +	if (p) { +		INIT_WORK(&p->work, tape_med_state_work_handler); +		p->device = tape_get_device(device); +		p->state = state; +		schedule_work(&p->work); +	} +} +  void  tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)  { -	if (device->medium_state == newstate) +	enum tape_medium_state oldstate; + +	oldstate = device->medium_state; +	if (oldstate == newstate)  		return; +	device->medium_state = newstate;  	switch(newstate){  	case MS_UNLOADED:  		device->tape_generic_status |= GMT_DR_OPEN(~0); -		if (device->medium_state == MS_LOADED) -			pr_info("%s: The tape cartridge has been successfully " -				"unloaded\n", dev_name(&device->cdev->dev)); +		if (oldstate == MS_LOADED) +			tape_med_state_work(device, MS_UNLOADED);  		break;  	case MS_LOADED:  		device->tape_generic_status &= ~GMT_DR_OPEN(~0); -		if (device->medium_state == MS_UNLOADED) -			pr_info("%s: A tape cartridge has been mounted\n", -				dev_name(&device->cdev->dev)); +		if (oldstate == MS_UNLOADED) +			tape_med_state_work(device, MS_LOADED);  		break;  	default: -		// print nothing  		break;  	} -	device->medium_state = newstate;  	wake_up(&device->state_change_wq);  } @@ -351,9 +400,6 @@ tape_generic_online(struct tape_device *device,  	rc = tapechar_setup_device(device);  	if (rc)  		goto out_minor; -	rc = tapeblock_setup_device(device); -	if (rc) -		goto out_char;  	tape_state_set(device, TS_UNUSED); @@ -361,8 +407,6 @@ tape_generic_online(struct tape_device *device,  	return 0; -out_char: -	tapechar_cleanup_device(device);  out_minor:  	tape_remove_minor(device);  out_discipline: @@ -376,7 +420,6 @@ out:  static void  tape_cleanup_device(struct tape_device *device)  { -	tapeblock_cleanup_device(device);  	tapechar_cleanup_device(device);  	device->discipline->cleanup_device(device);  	module_put(device->discipline->owner); @@ -735,10 +778,6 @@ __tape_start_io(struct tape_device *device, struct tape_request *request)  {  	int rc; -#ifdef CONFIG_S390_TAPE_BLOCK -	if (request->op == TO_BLOCK) -		device->discipline->check_locate(device, request); -#endif  	rc = ccw_device_start(  		device->cdev,  		request->cpaddr, @@ -1203,7 +1242,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)  }  /* - * Tape device open function used by tape_char & tape_block frontends. + * Tape device open function used by tape_char frontend.   */  int  tape_open(struct tape_device *device) @@ -1233,7 +1272,7 @@ tape_open(struct tape_device *device)  }  /* - * Tape device release function used by tape_char & tape_block frontends. + * Tape device release function used by tape_char frontend.   */  int  tape_release(struct tape_device *device) @@ -1294,7 +1333,6 @@ tape_init (void)  	DBF_EVENT(3, "tape init\n");  	tape_proc_init();  	tapechar_init (); -	tapeblock_init ();  	return 0;  } @@ -1308,7 +1346,6 @@ tape_exit(void)  	/* Get rid of the frontends */  	tapechar_exit(); -	tapeblock_exit();  	tape_proc_cleanup();  	debug_unregister (TAPE_DBF_AREA);  } diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c index 0ceb37984f7..8733b232a11 100644 --- a/drivers/s390/char/tape_proc.c +++ b/drivers/s390/char/tape_proc.c @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/tape.c   *    tape device driver for S/390 and zSeries tapes.   *   *  S390 and zSeries version - *    Copyright (C) 2001 IBM Corporation + *    Copyright IBM Corp. 2001   *    Author(s): Carsten Otte <cotte@de.ibm.com>   *		 Michael Holzheu <holzheu@de.ibm.com>   *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 3c3f342149e..3478e19ae19 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -1,9 +1,8 @@  /* - *  drivers/s390/char/tape_std.c   *    standard tape device functions for ibm tapes.   *   *  S390 and zSeries version - *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + *    Copyright IBM Corp. 2001, 2002   *    Author(s): Carsten Otte <cotte@de.ibm.com>   *		 Michael Holzheu <holzheu@de.ibm.com>   *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> @@ -79,7 +78,8 @@ tape_std_assign(struct tape_device *device)  	rc = tape_do_io_interruptible(device, request); -	del_timer(&timeout); +	del_timer_sync(&timeout); +	destroy_timer_on_stack(&timeout);  	if (rc != 0) {  		DBF_EVENT(3, "%08x: assign failed - device might be busy\n", @@ -564,7 +564,6 @@ int  tape_std_mtreten(struct tape_device *device, int mt_count)  {  	struct tape_request *request; -	int rc;  	request = tape_alloc_request(4, 0);  	if (IS_ERR(request)) @@ -576,7 +575,7 @@ tape_std_mtreten(struct tape_device *device, int mt_count)  	tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL);  	tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr);  	/* execute it, MTRETEN rc gets ignored */ -	rc = tape_do_io_interruptible(device, request); +	tape_do_io_interruptible(device, request);  	tape_free_request(request);  	return tape_mtop(device, MTREW, 1);  } diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 1fc95235934..8c760c03683 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -1,8 +1,7 @@  /* - *  drivers/s390/char/tape_std.h   *    standard tape device functions for ibm tapes.   * - *    Copyright (C) IBM Corp. 2001,2006 + *    Copyright IBM Corp. 2001, 2006   *    Author(s): Carsten Otte <cotte@de.ibm.com>   *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>   *		 Martin Schwidefsky <schwidefsky@de.ibm.com> @@ -101,11 +100,7 @@ struct tape_request *tape_std_read_block(struct tape_device *, size_t);  void tape_std_read_backward(struct tape_device *device,  			    struct tape_request *request);  struct tape_request *tape_std_write_block(struct tape_device *, size_t); -struct tape_request *tape_std_bread(struct tape_device *, struct request *); -void tape_std_free_bread(struct tape_request *);  void tape_std_check_locate(struct tape_device *, struct tape_request *); -struct tape_request *tape_std_bwrite(struct request *, -				     struct tape_device *, int);  /* Some non-mtop commands. */  int tape_std_assign(struct tape_device *); diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 911822db614..e91b89dc6d1 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -1,11 +1,10 @@  /* - *  drivers/s390/char/tty3270.c   *    IBM/3270 Driver - tty functions.   *   *  Author(s):   *    Original 3270 Code for 2.4 written by Richard Hitt (UTS Global)   *    Rewritten for 2.5 by Martin Schwidefsky <schwidefsky@de.ibm.com> - *	-- Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + *	-- Copyright IBM Corp. 2003   */  #include <linux/module.h> @@ -16,6 +15,7 @@  #include <linux/init.h>  #include <linux/console.h>  #include <linux/interrupt.h> +#include <linux/workqueue.h>  #include <linux/slab.h>  #include <linux/bootmem.h> @@ -61,7 +61,7 @@ struct tty3270_line {   */  struct tty3270 {  	struct raw3270_view view; -	struct tty_struct *tty;		/* Pointer to tty structure */ +	struct tty_port port;  	void **freemem_pages;		/* Array of pages used for freemem. */  	struct list_head freemem;	/* List of free memory for strings. */ @@ -81,6 +81,8 @@ struct tty3270 {  	unsigned int highlight;		/* Blink/reverse/underscore */  	unsigned int f_color;		/* Foreground color */  	struct tty3270_line *screen; +	unsigned int n_model, n_cols, n_rows;	/* New model & size */ +	struct work_struct resize_work;  	/* Input stuff. */  	struct string *prompt;		/* Output string for input area. */ @@ -116,16 +118,14 @@ struct tty3270 {  #define TTY_UPDATE_ALL		16	/* Recreate screen. */  static void tty3270_update(struct tty3270 *); +static void tty3270_resize_work(struct work_struct *work);  /*   * Setup timeout for a device. On timeout trigger an update.   */  static void tty3270_set_timer(struct tty3270 *tp, int expires)  { -	if (expires == 0) -		del_timer(&tp->timer); -	else -		mod_timer(&tp->timer, jiffies + expires); +	mod_timer(&tp->timer, jiffies + expires);  }  /* @@ -324,11 +324,10 @@ tty3270_blank_line(struct tty3270 *tp)  static void  tty3270_write_callback(struct raw3270_request *rq, void *data)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(rq->view, struct tty3270, view); -	tp = (struct tty3270 *) rq->view;  	if (rq->rc != 0) { -		/* Write wasn't successfull. Refresh all. */ +		/* Write wasn't successful. Refresh all. */  		tp->update_flags = TTY_UPDATE_ALL;  		tty3270_set_timer(tp, 1);  	} @@ -450,10 +449,9 @@ tty3270_rcl_add(struct tty3270 *tp, char *input, int len)  static void  tty3270_rcl_backward(struct kbd_data *kbd)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);  	struct string *s; -	tp = kbd->tty->driver_data;  	spin_lock_bh(&tp->view.lock);  	if (tp->inattr == TF_INPUT) {  		if (tp->rcl_walk && tp->rcl_walk->prev != &tp->rcl_lines) @@ -478,9 +476,8 @@ tty3270_rcl_backward(struct kbd_data *kbd)  static void  tty3270_exit_tty(struct kbd_data *kbd)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port); -	tp = kbd->tty->driver_data;  	raw3270_deactivate_view(&tp->view);  } @@ -490,10 +487,9 @@ tty3270_exit_tty(struct kbd_data *kbd)  static void  tty3270_scroll_forward(struct kbd_data *kbd)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);  	int nr_up; -	tp = kbd->tty->driver_data;  	spin_lock_bh(&tp->view.lock);  	nr_up = tp->nr_up - tp->view.rows + 2;  	if (nr_up < 0) @@ -513,10 +509,9 @@ tty3270_scroll_forward(struct kbd_data *kbd)  static void  tty3270_scroll_backward(struct kbd_data *kbd)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(kbd->port, struct tty3270, port);  	int nr_up; -	tp = kbd->tty->driver_data;  	spin_lock_bh(&tp->view.lock);  	nr_up = tp->nr_up + tp->view.rows - 2;  	if (nr_up + tp->view.rows - 2 > tp->nr_lines) @@ -537,11 +532,10 @@ static void  tty3270_read_tasklet(struct raw3270_request *rrq)  {  	static char kreset_data = TW_KR; -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(rrq->view, struct tty3270, view);  	char *input;  	int len; -	tp = (struct tty3270 *) rrq->view;  	spin_lock_bh(&tp->view.lock);  	/*  	 * Two AID keys are special: For 0x7d (enter) the input line @@ -577,13 +571,10 @@ tty3270_read_tasklet(struct raw3270_request *rrq)  	raw3270_request_add_data(tp->kreset, &kreset_data, 1);  	raw3270_start(&tp->view, tp->kreset); -	/* Emit input string. */ -	if (tp->tty) { -		while (len-- > 0) -			kbd_keycode(tp->kbd, *input++); -		/* Emit keycode for AID byte. */ -		kbd_keycode(tp->kbd, 256 + tp->input->string[0]); -	} +	while (len-- > 0) +		kbd_keycode(tp->kbd, *input++); +	/* Emit keycode for AID byte. */ +	kbd_keycode(tp->kbd, 256 + tp->input->string[0]);  	raw3270_request_reset(rrq);  	xchg(&tp->read, rrq); @@ -596,9 +587,10 @@ tty3270_read_tasklet(struct raw3270_request *rrq)  static void  tty3270_read_callback(struct raw3270_request *rq, void *data)  { +	struct tty3270 *tp = container_of(rq->view, struct tty3270, view);  	raw3270_get_view(rq->view);  	/* Schedule tasklet to pass input to tty. */ -	tasklet_schedule(&((struct tty3270 *) rq->view)->readlet); +	tasklet_schedule(&tp->readlet);  }  /* @@ -635,9 +627,8 @@ tty3270_issue_read(struct tty3270 *tp, int lock)  static int  tty3270_activate(struct raw3270_view *view)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(view, struct tty3270, view); -	tp = (struct tty3270 *) view;  	tp->update_flags = TTY_UPDATE_ALL;  	tty3270_set_timer(tp, 1);  	return 0; @@ -646,9 +637,8 @@ tty3270_activate(struct raw3270_view *view)  static void  tty3270_deactivate(struct raw3270_view *view)  { -	struct tty3270 *tp; +	struct tty3270 *tp = container_of(view, struct tty3270, view); -	tp = (struct tty3270 *) view;  	del_timer(&tp->timer);  } @@ -690,6 +680,11 @@ tty3270_alloc_view(void)  	if (!tp->freemem_pages)  		goto out_tp;  	INIT_LIST_HEAD(&tp->freemem); +	INIT_LIST_HEAD(&tp->lines); +	INIT_LIST_HEAD(&tp->update); +	INIT_LIST_HEAD(&tp->rcl_lines); +	tp->rcl_max = 20; +  	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {  		tp->freemem_pages[pages] = (void *)  			__get_free_pages(GFP_KERNEL|GFP_DMA, 0); @@ -710,6 +705,15 @@ tty3270_alloc_view(void)  	tp->kbd = kbd_alloc();  	if (!tp->kbd)  		goto out_reset; + +	tty_port_init(&tp->port); +	setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, +		    (unsigned long) tp); +	tasklet_init(&tp->readlet, +		     (void (*)(unsigned long)) tty3270_read_tasklet, +		     (unsigned long) tp->read); +	INIT_WORK(&tp->resize_work, tty3270_resize_work); +  	return tp;  out_reset: @@ -722,6 +726,7 @@ out_pages:  	while (pages--)  		free_pages((unsigned long) tp->freemem_pages[pages], 0);  	kfree(tp->freemem_pages); +	tty_port_destroy(&tp->port);  out_tp:  	kfree(tp);  out_err: @@ -736,7 +741,6 @@ tty3270_free_view(struct tty3270 *tp)  {  	int pages; -	del_timer_sync(&tp->timer);  	kbd_free(tp->kbd);  	raw3270_request_free(tp->kreset);  	raw3270_request_free(tp->read); @@ -744,48 +748,103 @@ tty3270_free_view(struct tty3270 *tp)  	for (pages = 0; pages < TTY3270_STRING_PAGES; pages++)  		free_pages((unsigned long) tp->freemem_pages[pages], 0);  	kfree(tp->freemem_pages); +	tty_port_destroy(&tp->port);  	kfree(tp);  }  /*   * Allocate tty3270 screen.   */ -static int -tty3270_alloc_screen(struct tty3270 *tp) +static struct tty3270_line * +tty3270_alloc_screen(unsigned int rows, unsigned int cols)  { +	struct tty3270_line *screen;  	unsigned long size;  	int lines; -	size = sizeof(struct tty3270_line) * (tp->view.rows - 2); -	tp->screen = kzalloc(size, GFP_KERNEL); -	if (!tp->screen) +	size = sizeof(struct tty3270_line) * (rows - 2); +	screen = kzalloc(size, GFP_KERNEL); +	if (!screen)  		goto out_err; -	for (lines = 0; lines < tp->view.rows - 2; lines++) { -		size = sizeof(struct tty3270_cell) * tp->view.cols; -		tp->screen[lines].cells = kzalloc(size, GFP_KERNEL); -		if (!tp->screen[lines].cells) +	for (lines = 0; lines < rows - 2; lines++) { +		size = sizeof(struct tty3270_cell) * cols; +		screen[lines].cells = kzalloc(size, GFP_KERNEL); +		if (!screen[lines].cells)  			goto out_screen;  	} -	return 0; +	return screen;  out_screen:  	while (lines--) -		kfree(tp->screen[lines].cells); -	kfree(tp->screen); +		kfree(screen[lines].cells); +	kfree(screen);  out_err: -	return -ENOMEM; +	return ERR_PTR(-ENOMEM);  }  /*   * Free tty3270 screen.   */  static void -tty3270_free_screen(struct tty3270 *tp) +tty3270_free_screen(struct tty3270_line *screen, unsigned int rows)  {  	int lines; -	for (lines = 0; lines < tp->view.rows - 2; lines++) -		kfree(tp->screen[lines].cells); -	kfree(tp->screen); +	for (lines = 0; lines < rows - 2; lines++) +		kfree(screen[lines].cells); +	kfree(screen); +} + +/* + * Resize tty3270 screen + */ +static void tty3270_resize_work(struct work_struct *work) +{ +	struct tty3270 *tp = container_of(work, struct tty3270, resize_work); +	struct tty3270_line *screen, *oscreen; +	struct tty_struct *tty; +	unsigned int orows; +	struct winsize ws; + +	screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); +	if (IS_ERR(screen)) +		return; +	/* Switch to new output size */ +	spin_lock_bh(&tp->view.lock); +	oscreen = tp->screen; +	orows = tp->view.rows; +	tp->view.model = tp->n_model; +	tp->view.rows = tp->n_rows; +	tp->view.cols = tp->n_cols; +	tp->screen = screen; +	free_string(&tp->freemem, tp->prompt); +	free_string(&tp->freemem, tp->status); +	tty3270_create_prompt(tp); +	tty3270_create_status(tp); +	tp->nr_up = 0; +	while (tp->nr_lines < tp->view.rows - 2) +		tty3270_blank_line(tp); +	tp->update_flags = TTY_UPDATE_ALL; +	spin_unlock_bh(&tp->view.lock); +	tty3270_free_screen(oscreen, orows); +	tty3270_set_timer(tp, 1); +	/* Informat tty layer about new size */ +	tty = tty_port_tty_get(&tp->port); +	if (!tty) +		return; +	ws.ws_row = tp->view.rows - 2; +	ws.ws_col = tp->view.cols; +	tty_do_resize(tty, &ws); +} + +static void +tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) +{ +	struct tty3270 *tp = container_of(view, struct tty3270, view); + +	tp->n_model = model; +	tp->n_rows = rows; +	tp->n_cols = cols; +	schedule_work(&tp->resize_work);  }  /* @@ -794,16 +853,15 @@ tty3270_free_screen(struct tty3270 *tp)  static void  tty3270_release(struct raw3270_view *view)  { -	struct tty3270 *tp; -	struct tty_struct *tty; +	struct tty3270 *tp = container_of(view, struct tty3270, view); +	struct tty_struct *tty = tty_port_tty_get(&tp->port); -	tp = (struct tty3270 *) view; -	tty = tp->tty;  	if (tty) {  		tty->driver_data = NULL; -		tp->tty = tp->kbd->tty = NULL; +		tty_port_tty_set(&tp->port, NULL);  		tty_hangup(tty);  		raw3270_put_view(&tp->view); +		tty_kref_put(tty);  	}  } @@ -813,8 +871,11 @@ tty3270_release(struct raw3270_view *view)  static void  tty3270_free(struct raw3270_view *view)  { -	tty3270_free_screen((struct tty3270 *) view); -	tty3270_free_view((struct tty3270 *) view); +	struct tty3270 *tp = container_of(view, struct tty3270, view); + +	del_timer_sync(&tp->timer); +	tty3270_free_screen(tp->screen, tp->view.rows); +	tty3270_free_view(tp);  }  /* @@ -823,14 +884,12 @@ tty3270_free(struct raw3270_view *view)  static void  tty3270_del_views(void)  { -	struct tty3270 *tp;  	int i; -	for (i = 0; i < tty3270_max_index; i++) { -		tp = (struct tty3270 *) -			raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR); -		if (!IS_ERR(tp)) -			raw3270_del_view(&tp->view); +	for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { +		struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); +		if (!IS_ERR(view)) +			raw3270_del_view(view);  	}  } @@ -839,56 +898,40 @@ static struct raw3270_fn tty3270_fn = {  	.deactivate = tty3270_deactivate,  	.intv = (void *) tty3270_irq,  	.release = tty3270_release, -	.free = tty3270_free +	.free = tty3270_free, +	.resize = tty3270_resize  };  /* - * This routine is called whenever a 3270 tty is opened. + * This routine is called whenever a 3270 tty is opened first time.   */ -static int -tty3270_open(struct tty_struct *tty, struct file * filp) +static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty)  { +	struct raw3270_view *view;  	struct tty3270 *tp;  	int i, rc; -	if (tty->count > 1) -		return 0;  	/* Check if the tty3270 is already there. */ -	tp = (struct tty3270 *) -		raw3270_find_view(&tty3270_fn, -				  tty->index + RAW3270_FIRSTMINOR); -	if (!IS_ERR(tp)) { +	view = raw3270_find_view(&tty3270_fn, tty->index + RAW3270_FIRSTMINOR); +	if (!IS_ERR(view)) { +		tp = container_of(view, struct tty3270, view);  		tty->driver_data = tp;  		tty->winsize.ws_row = tp->view.rows - 2;  		tty->winsize.ws_col = tp->view.cols; -		tty->low_latency = 0; -		tp->tty = tty; -		tp->kbd->tty = tty; +		tp->port.low_latency = 0; +		/* why to reassign? */ +		tty_port_tty_set(&tp->port, tty);  		tp->inattr = TF_INPUT; -		return 0; +		return tty_port_install(&tp->port, driver, tty);  	}  	if (tty3270_max_index < tty->index + 1)  		tty3270_max_index = tty->index + 1; -	/* Quick exit if there is no device for tty->index. */ -	if (PTR_ERR(tp) == -ENODEV) -		return -ENODEV; -  	/* Allocate tty3270 structure on first open. */  	tp = tty3270_alloc_view();  	if (IS_ERR(tp))  		return PTR_ERR(tp); -	INIT_LIST_HEAD(&tp->lines); -	INIT_LIST_HEAD(&tp->update); -	INIT_LIST_HEAD(&tp->rcl_lines); -	tp->rcl_max = 20; -	setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, -		    (unsigned long) tp); -	tasklet_init(&tp->readlet,  -		     (void (*)(unsigned long)) tty3270_read_tasklet, -		     (unsigned long) tp->read); -  	rc = raw3270_add_view(&tp->view, &tty3270_fn,  			      tty->index + RAW3270_FIRSTMINOR);  	if (rc) { @@ -896,16 +939,17 @@ tty3270_open(struct tty_struct *tty, struct file * filp)  		return rc;  	} -	rc = tty3270_alloc_screen(tp); -	if (rc) { +	tp->screen = tty3270_alloc_screen(tp->view.rows, tp->view.cols); +	if (IS_ERR(tp->screen)) { +		rc = PTR_ERR(tp->screen);  		raw3270_put_view(&tp->view);  		raw3270_del_view(&tp->view); +		tty3270_free_view(tp);  		return rc;  	} -	tp->tty = tty; -	tty->low_latency = 0; -	tty->driver_data = tp; +	tty_port_tty_set(&tp->port, tty); +	tp->port.low_latency = 0;  	tty->winsize.ws_row = tp->view.rows - 2;  	tty->winsize.ws_col = tp->view.cols; @@ -917,7 +961,7 @@ tty3270_open(struct tty_struct *tty, struct file * filp)  	for (i = 0; i < tp->view.rows - 2; i++)  		tty3270_blank_line(tp); -	tp->kbd->tty = tty; +	tp->kbd->port = &tp->port;  	tp->kbd->fn_handler[KVAL(K_INCRCONSOLE)] = tty3270_exit_tty;  	tp->kbd->fn_handler[KVAL(K_SCROLLBACK)] = tty3270_scroll_backward;  	tp->kbd->fn_handler[KVAL(K_SCROLLFORW)] = tty3270_scroll_forward; @@ -925,6 +969,29 @@ tty3270_open(struct tty_struct *tty, struct file * filp)  	kbd_ascebc(tp->kbd, tp->view.ascebc);  	raw3270_activate_view(&tp->view); + +	rc = tty_port_install(&tp->port, driver, tty); +	if (rc) { +		raw3270_put_view(&tp->view); +		return rc; +	} + +	tty->driver_data = tp; + +	return 0; +} + +/* + * This routine is called whenever a 3270 tty is opened. + */ +static int +tty3270_open(struct tty_struct *tty, struct file *filp) +{ +	struct tty3270 *tp = tty->driver_data; +	struct tty_port *port = &tp->port; + +	port->count++; +	tty_port_tty_set(port, tty);  	return 0;  } @@ -935,18 +1002,24 @@ tty3270_open(struct tty_struct *tty, struct file * filp)  static void  tty3270_close(struct tty_struct *tty, struct file * filp)  { -	struct tty3270 *tp; +	struct tty3270 *tp = tty->driver_data;  	if (tty->count > 1)  		return; -	tp = (struct tty3270 *) tty->driver_data;  	if (tp) {  		tty->driver_data = NULL; -		tp->tty = tp->kbd->tty = NULL; -		raw3270_put_view(&tp->view); +		tty_port_tty_set(&tp->port, NULL);  	}  } +static void tty3270_cleanup(struct tty_struct *tty) +{ +	struct tty3270 *tp = tty->driver_data; + +	if (tp) +		raw3270_put_view(&tp->view); +} +  /*   * We always have room.   */ @@ -1391,7 +1464,7 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch)  			tty3270_lf(tp);  			break;  		case 'Z':		/* Respond ID. */ -			kbd_puts_queue(tp->tty, "\033[?6c"); +			kbd_puts_queue(&tp->port, "\033[?6c");  			break;  		case '7':		/* Save cursor position. */  			tp->saved_cx = tp->cx; @@ -1437,11 +1510,11 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch)  	tp->esc_state = ESnormal;  	if (ch == 'n' && !tp->esc_ques) {  		if (tp->esc_par[0] == 5)		/* Status report. */ -			kbd_puts_queue(tp->tty, "\033[0n"); +			kbd_puts_queue(&tp->port, "\033[0n");  		else if (tp->esc_par[0] == 6) {	/* Cursor report. */  			char buf[40];  			sprintf(buf, "\033[%d;%dR", tp->cy + 1, tp->cx + 1); -			kbd_puts_queue(tp->tty, buf); +			kbd_puts_queue(&tp->port, buf);  		}  		return;  	} @@ -1513,12 +1586,13 @@ tty3270_escape_sequence(struct tty3270 *tp, char ch)   * String write routine for 3270 ttys   */  static void -tty3270_do_write(struct tty3270 *tp, const unsigned char *buf, int count) +tty3270_do_write(struct tty3270 *tp, struct tty_struct *tty, +		const unsigned char *buf, int count)  {  	int i_msg, i;  	spin_lock_bh(&tp->view.lock); -	for (i_msg = 0; !tp->tty->stopped && i_msg < count; i_msg++) { +	for (i_msg = 0; !tty->stopped && i_msg < count; i_msg++) {  		if (tp->esc_state != 0) {  			/* Continue escape sequence. */  			tty3270_escape_sequence(tp, buf[i_msg]); @@ -1595,10 +1669,10 @@ tty3270_write(struct tty_struct * tty,  	if (!tp)  		return 0;  	if (tp->char_count > 0) { -		tty3270_do_write(tp, tp->char_buf, tp->char_count); +		tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);  		tp->char_count = 0;  	} -	tty3270_do_write(tp, buf, count); +	tty3270_do_write(tp, tty, buf, count);  	return count;  } @@ -1629,7 +1703,7 @@ tty3270_flush_chars(struct tty_struct *tty)  	if (!tp)  		return;  	if (tp->char_count > 0) { -		tty3270_do_write(tp, tp->char_buf, tp->char_count); +		tty3270_do_write(tp, tty, tp->char_buf, tp->char_count);  		tp->char_count = 0;  	}  } @@ -1718,9 +1792,8 @@ tty3270_wait_until_sent(struct tty_struct *tty, int timeout)  {  } -static int -tty3270_ioctl(struct tty_struct *tty, struct file *file, -	      unsigned int cmd, unsigned long arg) +static int tty3270_ioctl(struct tty_struct *tty, unsigned int cmd, +			 unsigned long arg)  {  	struct tty3270 *tp; @@ -1729,13 +1802,12 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,  		return -ENODEV;  	if (tty->flags & (1 << TTY_IO_ERROR))  		return -EIO; -	return kbd_ioctl(tp->kbd, file, cmd, arg); +	return kbd_ioctl(tp->kbd, cmd, arg);  }  #ifdef CONFIG_COMPAT -static long -tty3270_compat_ioctl(struct tty_struct *tty, struct file *file, -	      unsigned int cmd, unsigned long arg) +static long tty3270_compat_ioctl(struct tty_struct *tty, +				 unsigned int cmd, unsigned long arg)  {  	struct tty3270 *tp; @@ -1744,11 +1816,13 @@ tty3270_compat_ioctl(struct tty_struct *tty, struct file *file,  		return -ENODEV;  	if (tty->flags & (1 << TTY_IO_ERROR))  		return -EIO; -	return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg)); +	return kbd_ioctl(tp->kbd, cmd, (unsigned long)compat_ptr(arg));  }  #endif  static const struct tty_operations tty3270_ops = { +	.install = tty3270_install, +	.cleanup = tty3270_cleanup,  	.open = tty3270_open,  	.close = tty3270_close,  	.write = tty3270_write, @@ -1768,6 +1842,22 @@ static const struct tty_operations tty3270_ops = {  	.set_termios = tty3270_set_termios  }; +static void tty3270_create_cb(int minor) +{ +	tty_register_device(tty3270_driver, minor - RAW3270_FIRSTMINOR, NULL); +} + +static void tty3270_destroy_cb(int minor) +{ +	tty_unregister_device(tty3270_driver, minor - RAW3270_FIRSTMINOR); +} + +static struct raw3270_notifier tty3270_notifier = +{ +	.create = tty3270_create_cb, +	.destroy = tty3270_destroy_cb, +}; +  /*   * 3270 tty registration code called from tty_init().   * Most kernel services (incl. kmalloc) are available at this poimt. @@ -1777,24 +1867,26 @@ static int __init tty3270_init(void)  	struct tty_driver *driver;  	int ret; -	driver = alloc_tty_driver(RAW3270_MAXDEVS); -	if (!driver) -		return -ENOMEM; +	driver = tty_alloc_driver(RAW3270_MAXDEVS, +				  TTY_DRIVER_REAL_RAW | +				  TTY_DRIVER_DYNAMIC_DEV | +				  TTY_DRIVER_RESET_TERMIOS); +	if (IS_ERR(driver)) +		return PTR_ERR(driver);  	/*  	 * Initialize the tty_driver structure  	 * Entries in tty3270_driver that are NOT initialized:  	 * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc  	 */ -	driver->owner = THIS_MODULE; -	driver->driver_name = "ttyTUB"; -	driver->name = "ttyTUB"; +	driver->driver_name = "tty3270"; +	driver->name = "3270/tty";  	driver->major = IBM_TTY3270_MAJOR;  	driver->minor_start = RAW3270_FIRSTMINOR; +	driver->name_base = RAW3270_FIRSTMINOR;  	driver->type = TTY_DRIVER_TYPE_SYSTEM;  	driver->subtype = SYSTEM_TYPE_TTY;  	driver->init_termios = tty_std_termios; -	driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV;  	tty_set_operations(driver, &tty3270_ops);  	ret = tty_register_driver(driver);  	if (ret) { @@ -1802,6 +1894,7 @@ static int __init tty3270_init(void)  		return ret;  	}  	tty3270_driver = driver; +	raw3270_register_notifier(&tty3270_notifier);  	return 0;  } @@ -1810,9 +1903,11 @@ tty3270_exit(void)  {  	struct tty_driver *driver; +	raw3270_unregister_notifier(&tty3270_notifier);  	driver = tty3270_driver;  	tty3270_driver = NULL;  	tty_unregister_driver(driver); +	put_tty_driver(driver);  	tty3270_del_views();  } diff --git a/drivers/s390/char/tty3270.h b/drivers/s390/char/tty3270.h index 799da57f039..11141a8f897 100644 --- a/drivers/s390/char/tty3270.h +++ b/drivers/s390/char/tty3270.h @@ -1,6 +1,4 @@  /* - *  drivers/s390/char/tty3270.h - *   *    Copyright IBM Corp. 2007   *   */ diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index 31a3ccbb649..0fdedadff7b 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c @@ -1,5 +1,5 @@  /* - * Copyright IBM Corp. 2004,2010 + * Copyright IBM Corp. 2004, 2010   * Interface implementation for communication with the z/VM control program   *   * Author(s): Christian Borntraeger <borntraeger@de.ibm.com> @@ -13,9 +13,11 @@  #include <linux/fs.h>  #include <linux/init.h> +#include <linux/compat.h>  #include <linux/kernel.h>  #include <linux/miscdevice.h>  #include <linux/slab.h> +#include <linux/export.h>  #include <asm/compat.h>  #include <asm/cpcmd.h>  #include <asm/debug.h> diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h index 6a993948e18..1e29b041838 100644 --- a/drivers/s390/char/vmcp.h +++ b/drivers/s390/char/vmcp.h @@ -1,5 +1,5 @@  /* - * Copyright (C) 2004, 2005 IBM Corporation + * Copyright IBM Corp. 2004, 2005   * Interface implementation for communication with the z/VM control program   * Version 1.0   * Author(s): Christian Borntraeger <cborntra@de.ibm.com> diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 9f661426e4a..a8848db7b09 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -1,5 +1,4 @@  /* - * drivers/s390/char/vmlogrdr.c   *	character device driver for reading z/VM system service records   *   * @@ -21,7 +20,7 @@  #include <linux/types.h>  #include <linux/interrupt.h>  #include <linux/spinlock.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/uaccess.h>  #include <asm/cpcmd.h>  #include <asm/debug.h> @@ -30,7 +29,6 @@  #include <linux/kmod.h>  #include <linux/cdev.h>  #include <linux/device.h> -#include <linux/smp_lock.h>  #include <linux/string.h>  MODULE_AUTHOR @@ -249,27 +247,25 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,  	char cp_command[80];  	char cp_response[160];  	char *onoff, *qid_string; +	int rc; -	memset(cp_command, 0x00, sizeof(cp_command)); -	memset(cp_response, 0x00, sizeof(cp_response)); - -        onoff = ((action == 1) ? "ON" : "OFF"); +	onoff = ((action == 1) ? "ON" : "OFF");  	qid_string = ((recording_class_AB == 1) ? " QID * " : ""); -        /* +	/*  	 * The recording commands needs to be called with option QID  	 * for guests that have previlege classes A or B.  	 * Purging has to be done as separate step, because recording  	 * can't be switched on as long as records are on the queue.  	 * Doing both at the same time doesn't work.  	 */ - -	if (purge) { +	if (purge && (action == 1)) { +		memset(cp_command, 0x00, sizeof(cp_command)); +		memset(cp_response, 0x00, sizeof(cp_response));  		snprintf(cp_command, sizeof(cp_command),  			 "RECORDING %s PURGE %s",  			 logptr->recording_name,  			 qid_string); -  		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);  	} @@ -279,19 +275,33 @@ static int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,  		logptr->recording_name,  		onoff,  		qid_string); -  	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);  	/* The recording command will usually answer with 'Command complete'  	 * on success, but when the specific service was never connected  	 * before then there might be an additional informational message  	 * 'HCPCRC8072I Recording entry not found' before the -         * 'Command complete'. So I use strstr rather then the strncmp. +	 * 'Command complete'. So I use strstr rather then the strncmp.  	 */  	if (strstr(cp_response,"Command complete")) -		return 0; +		rc = 0;  	else -		return -EIO; +		rc = -EIO; +	/* +	 * If we turn recording off, we have to purge any remaining records +	 * afterwards, as a large number of queued records may impact z/VM +	 * performance. +	 */ +	if (purge && (action == 0)) { +		memset(cp_command, 0x00, sizeof(cp_command)); +		memset(cp_response, 0x00, sizeof(cp_response)); +		snprintf(cp_command, sizeof(cp_command), +			 "RECORDING %s PURGE %s", +			 logptr->recording_name, +			 qid_string); +		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); +	} +	return rc;  } @@ -303,7 +313,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)  	int ret;  	dev_num = iminor(inode); -	if (dev_num > MAXMINOR) +	if (dev_num >= MAXMINOR)  		return -ENODEV;  	logptr = &sys_ser[dev_num]; @@ -311,7 +321,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)  	 * only allow for blocking reads to be open  	 */  	if (filp->f_flags & O_NONBLOCK) -		return -ENOSYS; +		return -EOPNOTSUPP;  	/* Besure this device hasn't already been opened */  	spin_lock_bh(&logptr->priv_lock); @@ -645,10 +655,19 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,  	len = strlen(buf);  	return len;  } - -  static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,  		   NULL); +static struct attribute *vmlogrdr_drv_attrs[] = { +	&driver_attr_recording_status.attr, +	NULL, +}; +static struct attribute_group vmlogrdr_drv_attr_group = { +	.attrs = vmlogrdr_drv_attrs, +}; +static const struct attribute_group *vmlogrdr_drv_attr_groups[] = { +	&vmlogrdr_drv_attr_group, +	NULL, +};  static struct attribute *vmlogrdr_attrs[] = {  	&dev_attr_autopurge.attr, @@ -657,6 +676,13 @@ static struct attribute *vmlogrdr_attrs[] = {  	&dev_attr_recording.attr,  	NULL,  }; +static struct attribute_group vmlogrdr_attr_group = { +	.attrs = vmlogrdr_attrs, +}; +static const struct attribute_group *vmlogrdr_attr_groups[] = { +	&vmlogrdr_attr_group, +	NULL, +};  static int vmlogrdr_pm_prepare(struct device *dev)  { @@ -681,18 +707,14 @@ static const struct dev_pm_ops vmlogrdr_pm_ops = {  	.prepare = vmlogrdr_pm_prepare,  }; -static struct attribute_group vmlogrdr_attr_group = { -	.attrs = vmlogrdr_attrs, -}; -  static struct class *vmlogrdr_class;  static struct device_driver vmlogrdr_driver = {  	.name = "vmlogrdr",  	.bus  = &iucv_bus,  	.pm = &vmlogrdr_pm_ops, +	.groups = vmlogrdr_drv_attr_groups,  }; -  static int vmlogrdr_register_driver(void)  {  	int ret; @@ -706,21 +728,14 @@ static int vmlogrdr_register_driver(void)  	if (ret)  		goto out_iucv; -	ret = driver_create_file(&vmlogrdr_driver, -				 &driver_attr_recording_status); -	if (ret) -		goto out_driver; -  	vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");  	if (IS_ERR(vmlogrdr_class)) {  		ret = PTR_ERR(vmlogrdr_class);  		vmlogrdr_class = NULL; -		goto out_attr; +		goto out_driver;  	}  	return 0; -out_attr: -	driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);  out_driver:  	driver_unregister(&vmlogrdr_driver);  out_iucv: @@ -734,7 +749,6 @@ static void vmlogrdr_unregister_driver(void)  {  	class_destroy(vmlogrdr_class);  	vmlogrdr_class = NULL; -	driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);  	driver_unregister(&vmlogrdr_driver);  	iucv_unregister(&vmlogrdr_iucv_handler, 1);  } @@ -747,10 +761,11 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)  	dev = kzalloc(sizeof(struct device), GFP_KERNEL);  	if (dev) { -		dev_set_name(dev, priv->internal_name); +		dev_set_name(dev, "%s", priv->internal_name);  		dev->bus = &iucv_bus;  		dev->parent = iucv_root;  		dev->driver = &vmlogrdr_driver; +		dev->groups = vmlogrdr_attr_groups;  		dev_set_drvdata(dev, priv);  		/*  		 * The release function could be called after the @@ -768,11 +783,6 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)  		return ret;  	} -	ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group); -	if (ret) { -		device_unregister(dev); -		return ret; -	}  	priv->class_device = device_create(vmlogrdr_class, dev,  					   MKDEV(vmlogrdr_major,  						 priv->minor_num), @@ -780,7 +790,6 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)  	if (IS_ERR(priv->class_device)) {  		ret = PTR_ERR(priv->class_device);  		priv->class_device=NULL; -		sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);  		device_unregister(dev);  		return ret;  	} @@ -793,7 +802,6 @@ static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)  {  	device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));  	if (priv->device != NULL) { -		sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);  		device_unregister(priv->device);  		priv->device=NULL;  	} diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 1de672f2103..0efb27f6f19 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -13,7 +13,7 @@  #include <linux/cdev.h>  #include <linux/slab.h> -#include <linux/smp_lock.h> +#include <linux/module.h>  #include <asm/uaccess.h>  #include <asm/cio.h> @@ -64,14 +64,17 @@ static int ur_set_offline(struct ccw_device *cdev);  static int ur_pm_suspend(struct ccw_device *cdev);  static struct ccw_driver ur_driver = { -	.name		= "vmur", -	.owner		= THIS_MODULE, +	.driver = { +		.name	= "vmur", +		.owner	= THIS_MODULE, +	},  	.ids		= ur_ids,  	.probe		= ur_probe,  	.remove		= ur_remove,  	.set_online	= ur_set_online,  	.set_offline	= ur_set_offline,  	.freeze		= ur_pm_suspend, +	.int_class	= IRQIO_VMR,  };  static DEFINE_MUTEX(vmur_mutex); @@ -86,7 +89,7 @@ static DEFINE_MUTEX(vmur_mutex);   * urd references:   * - ur_probe gets a urd reference, ur_remove drops the reference   *   dev_get_drvdata(&cdev->dev) - * - ur_open gets a urd reference, ur_relase drops the reference + * - ur_open gets a urd reference, ur_release drops the reference   *   (urf->urd)   *   * cdev references: @@ -700,7 +703,7 @@ static int ur_open(struct inode *inode, struct file *file)  	 * We treat the minor number as the devno of the ur device  	 * to find in the driver tree.  	 */ -	devno = MINOR(file->f_dentry->d_inode->i_rdev); +	devno = MINOR(file_inode(file)->i_rdev);  	urd = urdev_get_from_devno(devno);  	if (!urd) { @@ -900,7 +903,7 @@ static int ur_set_online(struct ccw_device *cdev)  		goto fail_urdev_put;  	} -	cdev_init(urd->char_device, &ur_fops); +	urd->char_device->ops = &ur_fops;  	urd->char_device->dev = MKDEV(major, minor);  	urd->char_device->owner = ur_fops.owner; @@ -919,8 +922,8 @@ static int ur_set_online(struct ccw_device *cdev)  		goto fail_free_cdev;  	} -	urd->device = device_create(vmur_class, NULL, urd->char_device->dev, -				    NULL, "%s", node_id); +	urd->device = device_create(vmur_class, &cdev->dev, +				    urd->char_device->dev, NULL, "%s", node_id);  	if (IS_ERR(urd->device)) {  		rc = PTR_ERR(urd->device);  		TRACE("ur_set_online: device_create rc=%d\n", rc); diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c deleted file mode 100644 index 12ef9121d4f..00000000000 --- a/drivers/s390/char/vmwatchdog.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Watchdog implementation based on z/VM Watchdog Timer API - * - * Copyright IBM Corp. 2004,2009 - * - * The user space watchdog daemon can use this driver as - * /dev/vmwatchdog to have z/VM execute the specified CP - * command when the timeout expires. The default command is - * "IPL", which which cause an immediate reboot. - */ -#define KMSG_COMPONENT "vmwatchdog" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#include <linux/init.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/slab.h> -#include <linux/suspend.h> -#include <linux/watchdog.h> - -#include <asm/ebcdic.h> -#include <asm/io.h> -#include <asm/uaccess.h> - -#define MAX_CMDLEN 240 -#define MIN_INTERVAL 15 -static char vmwdt_cmd[MAX_CMDLEN] = "IPL"; -static int vmwdt_conceal; - -static int vmwdt_nowayout = WATCHDOG_NOWAYOUT; - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); -MODULE_DESCRIPTION("z/VM Watchdog Timer"); -module_param_string(cmd, vmwdt_cmd, MAX_CMDLEN, 0644); -MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers"); -module_param_named(conceal, vmwdt_conceal, bool, 0644); -MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog " -		" is active"); -module_param_named(nowayout, vmwdt_nowayout, bool, 0); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started" -		" (default=CONFIG_WATCHDOG_NOWAYOUT)"); -MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); - -static unsigned int vmwdt_interval = 60; -static unsigned long vmwdt_is_open; -static int vmwdt_expect_close; - -static DEFINE_MUTEX(vmwdt_mutex); - -#define VMWDT_OPEN	0	/* devnode is open or suspend in progress */ -#define VMWDT_RUNNING	1	/* The watchdog is armed */ - -enum vmwdt_func { -	/* function codes */ -	wdt_init   = 0, -	wdt_change = 1, -	wdt_cancel = 2, -	/* flags */ -	wdt_conceal = 0x80000000, -}; - -static int __diag288(enum vmwdt_func func, unsigned int timeout, -			    char *cmd, size_t len) -{ -	register unsigned long __func asm("2") = func; -	register unsigned long __timeout asm("3") = timeout; -	register unsigned long __cmdp asm("4") = virt_to_phys(cmd); -	register unsigned long __cmdl asm("5") = len; -	int err; - -	err = -EINVAL; -	asm volatile( -		"	diag	%1,%3,0x288\n" -		"0:	la	%0,0\n" -		"1:\n" -		EX_TABLE(0b,1b) -		: "+d" (err) : "d"(__func), "d"(__timeout), -		  "d"(__cmdp), "d"(__cmdl) : "1", "cc"); -	return err; -} - -static int vmwdt_keepalive(void) -{ -	/* we allocate new memory every time to avoid having -	 * to track the state. static allocation is not an -	 * option since that might not be contiguous in real -	 * storage in case of a modular build */ -	static char *ebc_cmd; -	size_t len; -	int ret; -	unsigned int func; - -	ebc_cmd = kmalloc(MAX_CMDLEN, GFP_KERNEL); -	if (!ebc_cmd) -		return -ENOMEM; - -	len = strlcpy(ebc_cmd, vmwdt_cmd, MAX_CMDLEN); -	ASCEBC(ebc_cmd, MAX_CMDLEN); -	EBC_TOUPPER(ebc_cmd, MAX_CMDLEN); - -	func = vmwdt_conceal ? (wdt_init | wdt_conceal) : wdt_init; -	set_bit(VMWDT_RUNNING, &vmwdt_is_open); -	ret = __diag288(func, vmwdt_interval, ebc_cmd, len); -	WARN_ON(ret != 0); -	kfree(ebc_cmd); -	return ret; -} - -static int vmwdt_disable(void) -{ -	int ret = __diag288(wdt_cancel, 0, "", 0); -	WARN_ON(ret != 0); -	clear_bit(VMWDT_RUNNING, &vmwdt_is_open); -	return ret; -} - -static int __init vmwdt_probe(void) -{ -	/* there is no real way to see if the watchdog is supported, -	 * so we try initializing it with a NOP command ("BEGIN") -	 * that won't cause any harm even if the following disable -	 * fails for some reason */ -	static char __initdata ebc_begin[] = { -		194, 197, 199, 201, 213 -	}; -	if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) -		return -EINVAL; -	return vmwdt_disable(); -} - -static int vmwdt_open(struct inode *i, struct file *f) -{ -	int ret; -	if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) -		return -EBUSY; -	ret = vmwdt_keepalive(); -	if (ret) -		clear_bit(VMWDT_OPEN, &vmwdt_is_open); -	return ret ? ret : nonseekable_open(i, f); -} - -static int vmwdt_close(struct inode *i, struct file *f) -{ -	if (vmwdt_expect_close == 42) -		vmwdt_disable(); -	vmwdt_expect_close = 0; -	clear_bit(VMWDT_OPEN, &vmwdt_is_open); -	return 0; -} - -static struct watchdog_info vmwdt_info = { -	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, -	.firmware_version = 0, -	.identity = "z/VM Watchdog Timer", -}; - -static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg) -{ -	switch (cmd) { -	case WDIOC_GETSUPPORT: -		if (copy_to_user((void __user *)arg, &vmwdt_info, -					sizeof(vmwdt_info))) -			return -EFAULT; -		return 0; -	case WDIOC_GETSTATUS: -	case WDIOC_GETBOOTSTATUS: -		return put_user(0, (int __user *)arg); -	case WDIOC_GETTEMP: -		return -EINVAL; -	case WDIOC_SETOPTIONS: -		{ -			int options, ret; -			if (get_user(options, (int __user *)arg)) -				return -EFAULT; -			ret = -EINVAL; -			if (options & WDIOS_DISABLECARD) { -				ret = vmwdt_disable(); -				if (ret) -					return ret; -			} -			if (options & WDIOS_ENABLECARD) { -				ret = vmwdt_keepalive(); -			} -			return ret; -		} -	case WDIOC_GETTIMEOUT: -		return put_user(vmwdt_interval, (int __user *)arg); -	case WDIOC_SETTIMEOUT: -		{ -			int interval; -			if (get_user(interval, (int __user *)arg)) -				return -EFAULT; -			if (interval < MIN_INTERVAL) -				return -EINVAL; -			vmwdt_interval = interval; -		} -		return vmwdt_keepalive(); -	case WDIOC_KEEPALIVE: -		return vmwdt_keepalive(); -	} -	return -EINVAL; -} - -static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ -	int rc; - -	mutex_lock(&vmwdt_mutex); -	rc = __vmwdt_ioctl(cmd, arg); -	mutex_unlock(&vmwdt_mutex); -	return (long) rc; -} - -static ssize_t vmwdt_write(struct file *f, const char __user *buf, -				size_t count, loff_t *ppos) -{ -	if(count) { -		if (!vmwdt_nowayout) { -			size_t i; - -			/* note: just in case someone wrote the magic character -			 * five months ago... */ -			vmwdt_expect_close = 0; - -			for (i = 0; i != count; i++) { -				char c; -				if (get_user(c, buf+i)) -					return -EFAULT; -				if (c == 'V') -					vmwdt_expect_close = 42; -			} -		} -		/* someone wrote to us, we should restart timer */ -		vmwdt_keepalive(); -	} -	return count; -} - -static int vmwdt_resume(void) -{ -	clear_bit(VMWDT_OPEN, &vmwdt_is_open); -	return NOTIFY_DONE; -} - -/* - * It makes no sense to go into suspend while the watchdog is running. - * Depending on the memory size, the watchdog might trigger, while we - * are still saving the memory. - * We reuse the open flag to ensure that suspend and watchdog open are - * exclusive operations - */ -static int vmwdt_suspend(void) -{ -	if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) { -		pr_err("The system cannot be suspended while the watchdog" -			" is in use\n"); -		return NOTIFY_BAD; -	} -	if (test_bit(VMWDT_RUNNING, &vmwdt_is_open)) { -		clear_bit(VMWDT_OPEN, &vmwdt_is_open); -		pr_err("The system cannot be suspended while the watchdog" -			" is running\n"); -		return NOTIFY_BAD; -	} -	return NOTIFY_DONE; -} - -/* - * This function is called for suspend and resume. - */ -static int vmwdt_power_event(struct notifier_block *this, unsigned long event, -			     void *ptr) -{ -	switch (event) { -	case PM_POST_HIBERNATION: -	case PM_POST_SUSPEND: -		return vmwdt_resume(); -	case PM_HIBERNATION_PREPARE: -	case PM_SUSPEND_PREPARE: -		return vmwdt_suspend(); -	default: -		return NOTIFY_DONE; -	} -} - -static struct notifier_block vmwdt_power_notifier = { -	.notifier_call = vmwdt_power_event, -}; - -static const struct file_operations vmwdt_fops = { -	.open    = &vmwdt_open, -	.release = &vmwdt_close, -	.unlocked_ioctl = &vmwdt_ioctl, -	.write   = &vmwdt_write, -	.owner   = THIS_MODULE, -	.llseek  = noop_llseek, -}; - -static struct miscdevice vmwdt_dev = { -	.minor      = WATCHDOG_MINOR, -	.name       = "watchdog", -	.fops       = &vmwdt_fops, -}; - -static int __init vmwdt_init(void) -{ -	int ret; - -	ret = vmwdt_probe(); -	if (ret) -		return ret; -	ret = register_pm_notifier(&vmwdt_power_notifier); -	if (ret) -		return ret; -	/* -	 * misc_register() has to be the last action in module_init(), because -	 * file operations will be available right after this. -	 */ -	ret = misc_register(&vmwdt_dev); -	if (ret) { -		unregister_pm_notifier(&vmwdt_power_notifier); -		return ret; -	} -	return 0; -} -module_init(vmwdt_init); - -static void __exit vmwdt_exit(void) -{ -	unregister_pm_notifier(&vmwdt_power_notifier); -	misc_deregister(&vmwdt_dev); -} -module_exit(vmwdt_exit); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 3b94044027c..1884653e447 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -5,7 +5,7 @@   *   * For more information please refer to Documentation/s390/zfcpdump.txt   * - * Copyright IBM Corp. 2003,2008 + * Copyright IBM Corp. 2003, 2008   * Author(s): Michael Holzheu   */ @@ -16,11 +16,13 @@  #include <linux/slab.h>  #include <linux/miscdevice.h>  #include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/memblock.h> +  #include <asm/asm-offsets.h>  #include <asm/ipl.h>  #include <asm/sclp.h>  #include <asm/setup.h> -#include <asm/sigp.h>  #include <asm/uaccess.h>  #include <asm/debug.h>  #include <asm/processor.h> @@ -30,8 +32,8 @@  #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) -#define TO_USER		0 -#define TO_KERNEL	1 +#define TO_USER		1 +#define TO_KERNEL	0  #define CHUNK_INFO_SIZE	34 /* 2 16-byte char, each followed by blank */  enum arch_id { @@ -62,6 +64,7 @@ static struct dentry *zcore_dir;  static struct dentry *zcore_file;  static struct dentry *zcore_memmap_file;  static struct dentry *zcore_reipl_file; +static struct dentry *zcore_hsa_file;  static struct ipl_parameter_block *ipl_block;  /* @@ -72,11 +75,13 @@ static struct ipl_parameter_block *ipl_block;   * @count: Size of buffer, which should be copied   * @mode:  Either TO_KERNEL or TO_USER   */ -static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) +int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)  {  	int offs, blk_num;  	static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +	if (!hsa_available) +		return -ENODATA;  	if (count == 0)  		return 0; @@ -124,7 +129,7 @@ static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)  	}  	if (mode == TO_USER) {  		if (copy_to_user((__force __user void*) dest + offs, buf, -				 PAGE_SIZE)) +				 count - offs))  			return -EFAULT;  	} else  		memcpy(dest + offs, buf, count - offs); @@ -142,29 +147,13 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)  	return memcpy_hsa(dest, src, count, TO_KERNEL);  } -static int memcpy_real_user(void __user *dest, unsigned long src, size_t count) -{ -	static char buf[4096]; -	int offs = 0, size; - -	while (offs < count) { -		size = min(sizeof(buf), count - offs); -		if (memcpy_real(buf, (void *) src + offs, size)) -			return -EFAULT; -		if (copy_to_user(dest + offs, buf, size)) -			return -EFAULT; -		offs += size; -	} -	return 0; -} -  static int __init init_cpu_info(enum arch_id arch)  {  	struct save_area *sa;  	/* get info for boot cpu from lowcore, stored in the HSA */ -	sa = kmalloc(sizeof(*sa), GFP_KERNEL); +	sa = dump_save_area_create(0);  	if (!sa)  		return -ENOMEM;  	if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { @@ -172,7 +161,6 @@ static int __init init_cpu_info(enum arch_id arch)  		kfree(sa);  		return -EIO;  	} -	zfcpdump_save_areas[0] = sa;  	return 0;  } @@ -259,24 +247,25 @@ static int copy_lc(void __user *buf, void *sa, int sa_off, int len)  static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)  {  	unsigned long end; -	int i = 0; +	int i;  	if (count == 0)  		return 0;  	end = start + count; -	while (zfcpdump_save_areas[i]) { +	for (i = 0; i < dump_save_areas.count; i++) {  		unsigned long cp_start, cp_end; /* copy range */  		unsigned long sa_start, sa_end; /* save area range */  		unsigned long prefix;  		unsigned long sa_off, len, buf_off; +		struct save_area *save_area = dump_save_areas.areas[i]; -		prefix = zfcpdump_save_areas[i]->pref_reg; +		prefix = save_area->pref_reg;  		sa_start = prefix + sys_info.sa_base;  		sa_end = prefix + sys_info.sa_base + sys_info.sa_size;  		if ((end < sa_start) || (start > sa_end)) -			goto next; +			continue;  		cp_start = max(start, sa_start);  		cp_end = min(end, sa_end); @@ -285,15 +274,22 @@ static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)  		len = cp_end - cp_start;  		TRACE("copy_lc for: %lx\n", start); -		if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len)) +		if (copy_lc(buf + buf_off, save_area, sa_off, len))  			return -EFAULT; -next: -		i++;  	}  	return 0;  }  /* + * Release the HSA + */ +static void release_hsa(void) +{ +	diag308(DIAG308_REL_HSA, NULL); +	hsa_available = 0; +} + +/*   * Read routine for zcore character device   * First 4K are dump header   * Next 32MB are HSA Memory @@ -334,9 +330,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,  	mem_offs = 0;  	/* Copy from HSA data */ -	if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) { -		size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE -			   - mem_start)); +	if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) { +		size = min((count - hdr_count), +			   (size_t) (sclp_get_hsa_size() - mem_start));  		rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);  		if (rc)  			goto fail; @@ -346,8 +342,8 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,  	/* Copy from real mem */  	size = count - mem_offs - hdr_count; -	rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs, -			      size); +	rc = copy_to_user_real(buf + hdr_count + mem_offs, +			       (void *) mem_start + mem_offs, size);  	if (rc)  		goto fail; @@ -379,8 +375,8 @@ static int zcore_open(struct inode *inode, struct file *filp)  static int zcore_release(struct inode *inode, struct file *filep)  { -	diag308(DIAG308_REL_HSA, NULL); -	hsa_available = 0; +	if (hsa_available) +		release_hsa();  	return 0;  } @@ -417,33 +413,24 @@ static ssize_t zcore_memmap_read(struct file *filp, char __user *buf,  				 size_t count, loff_t *ppos)  {  	return simple_read_from_buffer(buf, count, ppos, filp->private_data, -				       MEMORY_CHUNKS * CHUNK_INFO_SIZE); +				       memblock.memory.cnt * CHUNK_INFO_SIZE);  }  static int zcore_memmap_open(struct inode *inode, struct file *filp)  { -	int i; +	struct memblock_region *reg;  	char *buf; -	struct mem_chunk *chunk_array; +	int i = 0; -	chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk), -			      GFP_KERNEL); -	if (!chunk_array) -		return -ENOMEM; -	detect_memory_layout(chunk_array); -	buf = kzalloc(MEMORY_CHUNKS * CHUNK_INFO_SIZE, GFP_KERNEL); +	buf = kzalloc(memblock.memory.cnt * CHUNK_INFO_SIZE, GFP_KERNEL);  	if (!buf) { -		kfree(chunk_array);  		return -ENOMEM;  	} -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		sprintf(buf + (i * CHUNK_INFO_SIZE), "%016llx %016llx ", -			(unsigned long long) chunk_array[i].addr, -			(unsigned long long) chunk_array[i].size); -		if (chunk_array[i].size == 0) -			break; +	for_each_memblock(memory, reg) { +		sprintf(buf + (i++ * CHUNK_INFO_SIZE), "%016llx %016llx ", +			(unsigned long long) reg->base, +			(unsigned long long) reg->size);  	} -	kfree(chunk_array);  	filp->private_data = buf;  	return nonseekable_open(inode, filp);  } @@ -490,6 +477,41 @@ static const struct file_operations zcore_reipl_fops = {  	.llseek		= no_llseek,  }; +static ssize_t zcore_hsa_read(struct file *filp, char __user *buf, +			      size_t count, loff_t *ppos) +{ +	static char str[18]; + +	if (hsa_available) +		snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size()); +	else +		snprintf(str, sizeof(str), "0\n"); +	return simple_read_from_buffer(buf, count, ppos, str, strlen(str)); +} + +static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf, +			       size_t count, loff_t *ppos) +{ +	char value; + +	if (*ppos != 0) +		return -EPIPE; +	if (copy_from_user(&value, buf, 1)) +		return -EFAULT; +	if (value != '0') +		return -EINVAL; +	release_hsa(); +	return count; +} + +static const struct file_operations zcore_hsa_fops = { +	.owner		= THIS_MODULE, +	.write		= zcore_hsa_write, +	.read		= zcore_hsa_read, +	.open		= nonseekable_open, +	.llseek		= no_llseek, +}; +  #ifdef CONFIG_32BIT  static void __init set_lc_mask(struct save_area *map) @@ -526,7 +548,7 @@ static void __init set_lc_mask(struct save_area *map)  /*   * Initialize dump globals for a given architecture   */ -static int __init sys_info_init(enum arch_id arch) +static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)  {  	int rc; @@ -548,75 +570,55 @@ static int __init sys_info_init(enum arch_id arch)  	rc = init_cpu_info(arch);  	if (rc)  		return rc; -	sys_info.mem_size = real_memory_size; +	sys_info.mem_size = mem_end;  	return 0;  }  static int __init check_sdias(void)  { -	int rc, act_hsa_size; - -	rc = sclp_sdias_blk_count(); -	if (rc < 0) { +	if (!sclp_get_hsa_size()) {  		TRACE("Could not determine HSA size\n"); -		return rc; -	} -	act_hsa_size = (rc - 1) * PAGE_SIZE; -	if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { -		TRACE("HSA size too small: %i\n", act_hsa_size); -		return -EINVAL; +		return -ENODEV;  	}  	return 0;  } -static int __init get_mem_size(unsigned long *mem) +static int __init get_mem_info(unsigned long *mem, unsigned long *end)  { -	int i; -	struct mem_chunk *chunk_array; +	struct memblock_region *reg; -	chunk_array = kzalloc(MEMORY_CHUNKS * sizeof(struct mem_chunk), -			      GFP_KERNEL); -	if (!chunk_array) -		return -ENOMEM; -	detect_memory_layout(chunk_array); -	for (i = 0; i < MEMORY_CHUNKS; i++) { -		if (chunk_array[i].size == 0) -			break; -		*mem += chunk_array[i].size; +	for_each_memblock(memory, reg) { +		*mem += reg->size; +		*end = max_t(unsigned long, *end, reg->base + reg->size);  	} -	kfree(chunk_array);  	return 0;  } -static int __init zcore_header_init(int arch, struct zcore_header *hdr) +static void __init zcore_header_init(int arch, struct zcore_header *hdr, +				     unsigned long mem_size)  { -	int rc, i; -	unsigned long memory = 0;  	u32 prefix; +	int i;  	if (arch == ARCH_S390X)  		hdr->arch_id = DUMP_ARCH_S390X;  	else  		hdr->arch_id = DUMP_ARCH_S390; -	rc = get_mem_size(&memory); -	if (rc) -		return rc; -	hdr->mem_size = memory; -	hdr->rmem_size = memory; +	hdr->mem_size = mem_size; +	hdr->rmem_size = mem_size;  	hdr->mem_end = sys_info.mem_size; -	hdr->num_pages = memory / PAGE_SIZE; -	hdr->tod = get_clock(); +	hdr->num_pages = mem_size / PAGE_SIZE; +	hdr->tod = get_tod_clock();  	get_cpu_id(&hdr->cpu_id); -	for (i = 0; zfcpdump_save_areas[i]; i++) { -		prefix = zfcpdump_save_areas[i]->pref_reg; +	for (i = 0; i < dump_save_areas.count; i++) { +		prefix = dump_save_areas.areas[i]->pref_reg;  		hdr->real_cpu_cnt++;  		if (!prefix)  			continue;  		hdr->lc_vec[hdr->cpu_cnt] = prefix;  		hdr->cpu_cnt++;  	} -	return 0;  }  /* @@ -636,7 +638,7 @@ static int __init zcore_reipl_init(void)  	ipl_block = (void *) __get_free_page(GFP_KERNEL);  	if (!ipl_block)  		return -ENOMEM; -	if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE) +	if (ipib_info.ipib < sclp_get_hsa_size())  		rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);  	else  		rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE); @@ -651,11 +653,15 @@ static int __init zcore_reipl_init(void)  static int __init zcore_init(void)  { +	unsigned long mem_size, mem_end;  	unsigned char arch;  	int rc; +	mem_size = mem_end = 0;  	if (ipl_info.type != IPL_TYPE_FCP_DUMP)  		return -ENODATA; +	if (OLDMEM_BASE) +		return -ENODATA;  	zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));  	debug_register_view(zcore_dbf, &debug_sprintf_view); @@ -672,6 +678,7 @@ static int __init zcore_init(void)  	rc = check_sdias();  	if (rc)  		goto fail; +	hsa_available = 1;  	rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);  	if (rc) @@ -693,13 +700,14 @@ static int __init zcore_init(void)  	}  #endif /* CONFIG_64BIT */ -	rc = sys_info_init(arch); +	rc = get_mem_info(&mem_size, &mem_end);  	if (rc)  		goto fail; -	rc = zcore_header_init(arch, &zcore_header); +	rc = sys_info_init(arch, mem_end);  	if (rc)  		goto fail; +	zcore_header_init(arch, &zcore_header, mem_size);  	rc = zcore_reipl_init();  	if (rc) @@ -728,9 +736,16 @@ static int __init zcore_init(void)  		rc = -ENOMEM;  		goto fail_memmap_file;  	} -	hsa_available = 1; +	zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir, +					     NULL, &zcore_hsa_fops); +	if (!zcore_hsa_file) { +		rc = -ENOMEM; +		goto fail_reipl_file; +	}  	return 0; +fail_reipl_file: +	debugfs_remove(zcore_reipl_file);  fail_memmap_file:  	debugfs_remove(zcore_memmap_file);  fail_file: @@ -747,6 +762,7 @@ static void __exit zcore_exit(void)  	debug_unregister(zcore_dbf);  	sclp_sdias_exit();  	free_page((unsigned long) ipl_block); +	debugfs_remove(zcore_hsa_file);  	debugfs_remove(zcore_reipl_file);  	debugfs_remove(zcore_memmap_file);  	debugfs_remove(zcore_file);  | 
