[S390] ETR support.

This patch adds support for clock synchronization to an external time
reference (ETR). The external time reference sends an oscillator
signal and a synchronization signal every 2^20 microseconds to keep
the TOD clocks of all connected servers in sync. For availability
two ETR units can be connected to a machine. If the clock deviates
for more than the sync-check tolerance all cpus get a machine check
that indicates that the clock is out of sync. For the lovely details
how to get the clock back in sync see the code below.

Signed-off-by: Martin Schwidefsky <[email protected]>
This commit is contained in:
Martin Schwidefsky 2007-02-05 21:18:19 +01:00
commit d54853ef8c
13 changed files with 1488 additions and 109 deletions

View file

@ -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",

View file

@ -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->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
rc = get_sync_clock(&data->ep_sys_time);
/* Ignore return code if sync clock is switched off. */
if (rc == -ENOSYS || rc == -EACCES)
rc = 0;
data->ep_sys_time = get_clock ();
de_ccw->count = sizeof (struct DE_eckd_data);
de_ccw->flags |= CCW_FLAG_SLI;
return rc;
}
de_ccw->count = sizeof (struct DE_eckd_data);
de_ccw->flags |= CCW_FLAG_SLI;
}
return;
} /* end check_XRC */
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);

View file

@ -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;
}

View file

@ -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 */

View file

@ -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 */