diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-02-05 15:18:19 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-02-05 15:18:19 -0500 |
commit | d54853ef8cb17296ac7bce9c77430fb7c80532d0 (patch) | |
tree | 649e14d532e17231225a042a7c9a3d9207ad91ee /drivers/s390/block/dasd_eckd.c | |
parent | c1821c2e9711adc3cd298a16b7237c92a2cee78d (diff) |
[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 <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 44 |
1 files changed, 26 insertions, 18 deletions
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, | |||
204 | return 0; | 204 | return 0; |
205 | } | 205 | } |
206 | 206 | ||
207 | static inline void | 207 | static inline int |
208 | check_XRC (struct ccw1 *de_ccw, | 208 | check_XRC (struct ccw1 *de_ccw, |
209 | struct DE_eckd_data *data, | 209 | struct DE_eckd_data *data, |
210 | struct dasd_device *device) | 210 | struct dasd_device *device) |
211 | { | 211 | { |
212 | struct dasd_eckd_private *private; | 212 | struct dasd_eckd_private *private; |
213 | int rc; | ||
213 | 214 | ||
214 | private = (struct dasd_eckd_private *) device->private; | 215 | private = (struct dasd_eckd_private *) device->private; |
216 | if (!private->rdc_data.facilities.XRC_supported) | ||
217 | return 0; | ||
215 | 218 | ||
216 | /* switch on System Time Stamp - needed for XRC Support */ | 219 | /* switch on System Time Stamp - needed for XRC Support */ |
217 | if (private->rdc_data.facilities.XRC_supported) { | 220 | data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ |
218 | 221 | data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ | |
219 | data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */ | ||
220 | data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */ | ||
221 | |||
222 | data->ep_sys_time = get_clock (); | ||
223 | |||
224 | de_ccw->count = sizeof (struct DE_eckd_data); | ||
225 | de_ccw->flags |= CCW_FLAG_SLI; | ||
226 | } | ||
227 | 222 | ||
228 | return; | 223 | rc = get_sync_clock(&data->ep_sys_time); |
224 | /* Ignore return code if sync clock is switched off. */ | ||
225 | if (rc == -ENOSYS || rc == -EACCES) | ||
226 | rc = 0; | ||
229 | 227 | ||
230 | } /* end check_XRC */ | 228 | de_ccw->count = sizeof (struct DE_eckd_data); |
229 | de_ccw->flags |= CCW_FLAG_SLI; | ||
230 | return rc; | ||
231 | } | ||
231 | 232 | ||
232 | static inline void | 233 | static inline int |
233 | define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | 234 | define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, |
234 | int totrk, int cmd, struct dasd_device * device) | 235 | int totrk, int cmd, struct dasd_device * device) |
235 | { | 236 | { |
236 | struct dasd_eckd_private *private; | 237 | struct dasd_eckd_private *private; |
237 | struct ch_t geo, beg, end; | 238 | struct ch_t geo, beg, end; |
239 | int rc = 0; | ||
238 | 240 | ||
239 | private = (struct dasd_eckd_private *) device->private; | 241 | private = (struct dasd_eckd_private *) device->private; |
240 | 242 | ||
@@ -263,12 +265,12 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | |||
263 | case DASD_ECKD_CCW_WRITE_KD_MT: | 265 | case DASD_ECKD_CCW_WRITE_KD_MT: |
264 | data->mask.perm = 0x02; | 266 | data->mask.perm = 0x02; |
265 | data->attributes.operation = private->attrib.operation; | 267 | data->attributes.operation = private->attrib.operation; |
266 | check_XRC (ccw, data, device); | 268 | rc = check_XRC (ccw, data, device); |
267 | break; | 269 | break; |
268 | case DASD_ECKD_CCW_WRITE_CKD: | 270 | case DASD_ECKD_CCW_WRITE_CKD: |
269 | case DASD_ECKD_CCW_WRITE_CKD_MT: | 271 | case DASD_ECKD_CCW_WRITE_CKD_MT: |
270 | data->attributes.operation = DASD_BYPASS_CACHE; | 272 | data->attributes.operation = DASD_BYPASS_CACHE; |
271 | check_XRC (ccw, data, device); | 273 | rc = check_XRC (ccw, data, device); |
272 | break; | 274 | break; |
273 | case DASD_ECKD_CCW_ERASE: | 275 | case DASD_ECKD_CCW_ERASE: |
274 | case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: | 276 | case DASD_ECKD_CCW_WRITE_HOME_ADDRESS: |
@@ -276,7 +278,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, | |||
276 | data->mask.perm = 0x3; | 278 | data->mask.perm = 0x3; |
277 | data->mask.auth = 0x1; | 279 | data->mask.auth = 0x1; |
278 | data->attributes.operation = DASD_BYPASS_CACHE; | 280 | data->attributes.operation = DASD_BYPASS_CACHE; |
279 | check_XRC (ccw, data, device); | 281 | rc = check_XRC (ccw, data, device); |
280 | break; | 282 | break; |
281 | default: | 283 | default: |
282 | DEV_MESSAGE(KERN_ERR, device, "unknown opcode 0x%x", cmd); | 284 | 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, | |||
312 | data->beg_ext.head = beg.head; | 314 | data->beg_ext.head = beg.head; |
313 | data->end_ext.cyl = end.cyl; | 315 | data->end_ext.cyl = end.cyl; |
314 | data->end_ext.head = end.head; | 316 | data->end_ext.head = end.head; |
317 | return rc; | ||
315 | } | 318 | } |
316 | 319 | ||
317 | static inline void | 320 | static inline void |
@@ -1200,7 +1203,12 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req) | |||
1200 | return cqr; | 1203 | return cqr; |
1201 | ccw = cqr->cpaddr; | 1204 | ccw = cqr->cpaddr; |
1202 | /* First ccw is define extent. */ | 1205 | /* First ccw is define extent. */ |
1203 | define_extent(ccw++, cqr->data, first_trk, last_trk, cmd, device); | 1206 | if (define_extent(ccw++, cqr->data, first_trk, |
1207 | last_trk, cmd, device) == -EAGAIN) { | ||
1208 | /* Clock not in sync and XRC is enabled. Try again later. */ | ||
1209 | dasd_sfree_request(cqr, device); | ||
1210 | return ERR_PTR(-EAGAIN); | ||
1211 | } | ||
1204 | /* Build locate_record+read/write/ccws. */ | 1212 | /* Build locate_record+read/write/ccws. */ |
1205 | idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); | 1213 | idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data)); |
1206 | LO_data = (struct LO_eckd_data *) (idaws + cidaw); | 1214 | LO_data = (struct LO_eckd_data *) (idaws + cidaw); |