aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block/dasd_eckd.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2007-02-05 15:18:19 -0500
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-02-05 15:18:19 -0500
commitd54853ef8cb17296ac7bce9c77430fb7c80532d0 (patch)
tree649e14d532e17231225a042a7c9a3d9207ad91ee /drivers/s390/block/dasd_eckd.c
parentc1821c2e9711adc3cd298a16b7237c92a2cee78d (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.c44
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
207static inline void 207static inline int
208check_XRC (struct ccw1 *de_ccw, 208check_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
232static inline void 233static inline int
233define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk, 234define_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
317static inline void 320static 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);