diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/s390/block/dasd.c | 13 | ||||
| -rw-r--r-- | drivers/s390/block/dasd_eckd.c | 44 | ||||
| -rw-r--r-- | drivers/s390/cio/cio.c | 17 | ||||
| -rw-r--r-- | drivers/s390/s390mach.c | 17 | ||||
| -rw-r--r-- | drivers/s390/s390mach.h | 3 | 
5 files changed, 72 insertions, 22 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f208940c463c..555e18a6b781 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1232,6 +1232,19 @@ __dasd_process_blk_queue(struct dasd_device * device)  		if (IS_ERR(cqr)) {  			if (PTR_ERR(cqr) == -ENOMEM)  				break;	/* terminate request queue loop */ +			if (PTR_ERR(cqr) == -EAGAIN) { +				/* +				 * The current request cannot be build right +				 * now, we have to try later. If this request +				 * is the head-of-queue we stop the device +				 * for 1/2 second. +				 */ +				if (!list_empty(&device->ccw_queue)) +					break; +				device->stopped |= DASD_STOPPED_PENDING; +				dasd_set_timer(device, HZ/2); +				break; +			}  			DBF_DEV_EVENT(DBF_ERR, device,  				      "CCW creation failed (rc=%ld) "  				      "on request %p", diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index d59115cce6dc..a17d73193aab 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -204,37 +204,39 @@ recs_per_track(struct dasd_eckd_characteristics * rdc,  	return 0;  } -static inline void +static inline int  check_XRC (struct ccw1         *de_ccw,             struct DE_eckd_data *data,             struct dasd_device  *device)  {          struct dasd_eckd_private *private; +	int rc;          private = (struct dasd_eckd_private *) device->private; +	if (!private->rdc_data.facilities.XRC_supported) +		return 0;          /* switch on System Time Stamp - needed for XRC Support */ -        if (private->rdc_data.facilities.XRC_supported) { - -                data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */ -                data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ - -                data->ep_sys_time = get_clock (); - -                de_ccw->count = sizeof (struct DE_eckd_data); -		de_ccw->flags |= CCW_FLAG_SLI; -        } +	data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */ +	data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ -        return; +	rc = get_sync_clock(&data->ep_sys_time); +	/* Ignore return code if sync clock is switched off. */ +	if (rc == -ENOSYS || rc == -EACCES) +		rc = 0; -} /* end check_XRC */ +	de_ccw->count = sizeof (struct DE_eckd_data); +	de_ccw->flags |= CCW_FLAG_SLI; +	return rc; +} -static inline void +static inline int  define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,  	      int totrk, int cmd, struct dasd_device * device)  {  	struct dasd_eckd_private *private;  	struct ch_t geo, beg, end; +	int rc = 0;  	private = (struct dasd_eckd_private *) device->private; @@ -263,12 +265,12 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,  	case DASD_ECKD_CCW_WRITE_KD_MT:  		data->mask.perm = 0x02;  		data->attributes.operation = private->attrib.operation; -                check_XRC (ccw, data, device); +		rc = check_XRC (ccw, data, device);  		break;  	case DASD_ECKD_CCW_WRITE_CKD:  	case DASD_ECKD_CCW_WRITE_CKD_MT:  		data->attributes.operation = DASD_BYPASS_CACHE; -                check_XRC (ccw, data, device); +		rc = check_XRC (ccw, data, device);  		break;  	case DASD_ECKD_CCW_ERASE:  	case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: @@ -276,7 +278,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,  		data->mask.perm = 0x3;  		data->mask.auth = 0x1;  		data->attributes.operation = DASD_BYPASS_CACHE; -                check_XRC (ccw, data, device); +		rc = check_XRC (ccw, data, device);  		break;  	default:  		DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); @@ -312,6 +314,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,  	data->beg_ext.head = beg.head;  	data->end_ext.cyl = end.cyl;  	data->end_ext.head = end.head; +	return rc;  }  static inline void @@ -1200,7 +1203,12 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)  		return cqr;  	ccw = cqr->cpaddr;  	/* First ccw is define extent. */ -	define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device); +	if (define_extent(ccw++, cqr->data, first_trk, +			  last_trk, cmd, device) == -EAGAIN) { +		/* Clock not in sync and XRC is enabled. Try again later. */ +		dasd_sfree_request(cqr, device); +		return ERR_PTR(-EAGAIN); +	}  	/* Build locate_record+read/write/ccws. */  	idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));  	LO_data = (struct LO_eckd_data *) (idaws + cidaw); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 07a4cbfc2436..ad2b37929848 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -646,7 +646,7 @@ do_IRQ (struct pt_regs *regs)  		 * Make sure that the i/o interrupt did not "overtake"  		 * the last HZ timer interrupt.  		 */ -		account_ticks(); +		account_ticks(S390_lowcore.int_clock);  	/*  	 * Get interrupt information from lowcore  	 */ @@ -850,6 +850,19 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)  	return -EBUSY; /* uhm... */  } +/* we can't use the normal udelay here, since it enables external interrupts */ + +static void udelay_reset(unsigned long usecs) +{ +	uint64_t start_cc, end_cc; + +	asm volatile ("STCK %0" : "=m" (start_cc)); +	do { +		cpu_relax(); +		asm volatile ("STCK %0" : "=m" (end_cc)); +	} while (((end_cc - start_cc)/4096) < usecs); +} +  static inline int  __clear_subchannel_easy(struct subchannel_id schid)  { @@ -865,7 +878,7 @@ __clear_subchannel_easy(struct subchannel_id schid)  			if (schid_equal(&ti.schid, &schid))  				return 0;  		} -		udelay(100); +		udelay_reset(100);  	}  	return -EBUSY;  } diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 442d63470428..806bb1a921eb 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -15,7 +15,7 @@  #include <linux/time.h>  #include <linux/device.h>  #include <linux/kthread.h> - +#include <asm/etr.h>  #include <asm/lowcore.h>  #include <asm/cio.h>  #include "cio/cio.h" @@ -466,6 +466,19 @@ s390_do_machine_check(struct pt_regs *regs)  			s390_handle_damage("unable to revalidate registers.");  	} +	if (mci->cd) { +		/* Timing facility damage */ +		s390_handle_damage("TOD clock damaged"); +	} + +	if (mci->ed && mci->ec) { +		/* External damage */ +		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC)) +			etr_sync_check(); +		if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH)) +			etr_switch_to_local(); +	} +  	if (mci->se)  		/* Storage error uncorrected */  		s390_handle_damage("received storage error uncorrected " @@ -504,7 +517,7 @@ static int  machine_check_init(void)  {  	init_MUTEX_LOCKED(&m_sem); -	ctl_clear_bit(14, 25);	/* disable external damage MCH */ +	ctl_set_bit(14, 25);	/* enable external damage MCH */  	ctl_set_bit(14, 27);    /* enable system recovery MCH */  #ifdef CONFIG_MACHCHK_WARNING  	ctl_set_bit(14, 24);	/* enable warning MCH */ diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h index 7abb42a09ae2..d3ca4281a494 100644 --- a/drivers/s390/s390mach.h +++ b/drivers/s390/s390mach.h @@ -102,4 +102,7 @@ static inline int stcrw(struct crw *pcrw )  	return ccode;  } +#define ED_ETR_SYNC	12	/* External damage ETR sync check */ +#define ED_ETR_SWITCH	13	/* External damage ETR switch to local */ +  #endif /* __s390mach */  |