Motr  M0
chs.c
Go to the documentation of this file.
1 /* -*- C -*- */
2 /*
3  * Copyright (c) 2011-2020 Seagate Technology LLC and/or its Affiliates
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * For any questions about this software or licensing,
18  * please email opensource@seagate.com or cortx-questions@seagate.com.
19  *
20  */
21 
22 
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h> /* sqrt(3) */
27 
28 #include "lib/assert.h"
29 #include "desim/sim.h"
30 #include "desim/chs.h"
31 
37 long long int llabs(long long int j);
38 
39 static void chs_submit(struct storage_dev *dev,
41  sector_t sector, unsigned long count);
42 
43 M0_INTERNAL void chs_conf_init(struct chs_conf *conf)
44 {
45  sim_time_t track;
46  sim_time_t avg;
48 
49  unsigned nrcyl;
50  unsigned i;
51 
52  sector_t sectors_track;
53  sector_t sectors_cyl;
54  sector_t sectors_cum;
55  unsigned smax;
56  unsigned smin;
57  unsigned cyl_in_zone;
58 
59  /*
60  * Assuming seek time of the form
61  *
62  * seek_time(d) = track + alpha * (d - 1) + beta * sqrt(d - 1)
63  *
64  * where "d" is a number of cylinders to move over and "track" is a
65  * track-to-track seeking time, one obtains:
66  *
67  * alpha = ( 4 * full - 6 * avg + 2 * track)/nrcyl, and
68  * beta = (-3 * full + 6 * avg - 3 * track)/sqrt(nrcyl)
69  *
70  * where "full" is "full stroke" and "avg" is average seek times as
71  * reported by drive manufacturer, and nrcyl is a number of cylinders on
72  * the disc.
73  */
74 
75  track = conf->cc_seek_track_to_track;
76  avg = conf->cc_seek_avg;
77  full = conf->cc_seek_full_stroke;
78  nrcyl = conf->cc_cylinders;
79 
80  M0_ASSERT(4 * full + 2 * track >= 6 * avg);
81  M0_ASSERT(3 * full + 3 * track <= 6 * avg);
82 
83  conf->cc_alpha = ( 4 * full - 6 * avg + 2 * track) / nrcyl;
84  conf->cc_beta = (-3 * full + 6 * avg - 3 * track) / sqrt(nrcyl);
85 
86  /*
87  * Setup an array used to map sector number (LBA) to CHS
88  * coordinates. Modern drives use "zoning"---a technique where number of
89  * sectors in a track depends on a cylinder. Zoning influences drive
90  * behaviour (seek and rotational delays, as well as transfer times).
91  *
92  * In the simplest case of "linear zoning" where the number of sectors
93  * grows linearly with the zone number, the total number of sectors in
94  * all cylinders up to a given one is quadratic in cylinder number
95  * (because it is sum of arithmetic progression). Hence, reverse mapping
96  * (from LBA to cylinder number) is given as a solution to quadratic
97  * equation. The resulting rounding leads to inconsistencies. For
98  * simplicity, just allocate an array with data for every cylinder. This
99  * has an advantage or being generalizable to non-linear zoning.
100  */
101 
102  conf->cc_zone = calloc(nrcyl, sizeof conf->cc_zone[0]);
103  smax = conf->cc_sectors_max;
104  smin = conf->cc_sectors_min;
105 
106  cyl_in_zone = conf->cc_cyl_in_zone;
107  for (sectors_cum = 0, i = 0; i < nrcyl; ++i) {
108  unsigned zone_start;
109 
110  /* first cylinder in zone */
111  zone_start = i / cyl_in_zone * cyl_in_zone;
112  /* assume linear "zoning". */
113  sectors_track = smax - (smax - smin) * zone_start / (nrcyl - 1);
114  sectors_cyl = sectors_track * conf->cc_heads;
115  conf->cc_zone[i].track_sectors = sectors_track;
116  conf->cc_zone[i].cyl_sectors = sectors_cyl;
117  conf->cc_zone[i].cyl_first = sectors_cum;
118  sectors_cum += sectors_cyl;
119  }
120  printf("total of %llu sectors\n", sectors_cum);
121 }
122 
123 M0_INTERNAL void chs_conf_fini(struct chs_conf *conf)
124 {
125  if (conf->cc_zone != NULL)
126  free(conf->cc_zone);
127 }
128 
129 M0_INTERNAL void chs_dev_init(struct chs_dev *dev, struct sim *sim,
130  struct chs_conf *conf)
131 {
132  struct storage_dev *cd = &dev->cd_storage;
133  char *name = cd->sd_name;
134 
135  cd->sd_sim = dev->cd_todo.sc_sim = sim;
136  cd->sd_conf = &conf->cc_storage;
137  cd->sd_submit = &chs_submit;
138  dev->cd_conf = conf;
139  dev->cd_state = CDS_IDLE;
140  cnt_init(&dev->cd_seek_time, NULL, "seek-time@%s", name);
141  cnt_init(&dev->cd_rotation_time, NULL, "rotation-time@%s", name);
142  cnt_init(&dev->cd_xfer_time, NULL, "xfer-time@%s", name);
143  cnt_init(&dev->cd_read_size, NULL, "read-size@%s", name);
144  cnt_init(&dev->cd_write_size, NULL, "write-size@%s", name);
145 }
146 
147 M0_INTERNAL void chs_dev_fini(struct chs_dev *dev)
148 {
149  cnt_fini(&dev->cd_write_size);
150  cnt_fini(&dev->cd_read_size);
151  cnt_fini(&dev->cd_xfer_time);
152  cnt_fini(&dev->cd_rotation_time);
153  cnt_fini(&dev->cd_seek_time);
154 }
155 
156 /*
157  * Number of sectors at a track in a given cylinder
158  */
159 static unsigned chs_tracks(struct chs_conf *conf, unsigned cyl)
160 {
161  M0_ASSERT(cyl < conf->cc_cylinders);
162  return conf->cc_zone[cyl].track_sectors;
163 }
164 
165 /*
166  * Total number of sectors in cylinders up to, but not including given one.
167  */
168 static sector_t chs_cylinder_sectors(struct chs_conf *conf, unsigned cyl)
169 {
170  M0_ASSERT(cyl < conf->cc_cylinders);
171  return conf->cc_zone[cyl].cyl_first;
172 }
173 
174 /*
175  * A cylinder containing a given sector.
176  */
177 static unsigned chs_sector_cylinder(struct chs_conf *conf, sector_t sector)
178 {
179  unsigned i;
180  unsigned j;
181  unsigned h;
182 
183  M0_ASSERT(sector <= conf->cc_zone[conf->cc_cylinders - 1].cyl_first +
184  conf->cc_zone[conf->cc_cylinders - 1].cyl_sectors);
185 
186  i = 0;
187  j = conf->cc_cylinders;
188  while (i + 1 != j) {
189  h = (i + j) / 2;
190  if (conf->cc_zone[h].cyl_first <= sector)
191  i = h;
192  else
193  j = h;
194  }
195  M0_ASSERT(conf->cc_zone[i].cyl_first <= sector);
196  M0_ASSERT(i == conf->cc_cylinders - 1 ||
197  sector < conf->cc_zone[i + 1].cyl_first);
198  return i;
199 }
200 
201 /*
202  * Convert LBA into CHS base.
203  */
204 static void chs_sector_to_chs(struct chs_conf *conf, sector_t sector,
205  unsigned *head, unsigned *cylinder,
206  sector_t *sect_in_track)
207 {
208  unsigned cyl_sects;
209  unsigned track_sects;
210 
211  *cylinder = chs_sector_cylinder(conf, sector);
212  M0_ASSERT(*cylinder < conf->cc_cylinders);
213 
214  track_sects = chs_tracks(conf, *cylinder);
215  /* sectors in a cylinder */
216  cyl_sects = track_sects * conf->cc_heads;
217  M0_ASSERT(cyl_sects == conf->cc_zone[*cylinder].cyl_sectors);
218  sector -= chs_cylinder_sectors(conf, *cylinder);
219  M0_ASSERT(sector < cyl_sects);
220  *head = sector / track_sects;
221  M0_ASSERT(*head < conf->cc_heads);
222  sector -= *head * track_sects;
223  *sect_in_track = sector;
224  M0_ASSERT(*sect_in_track < track_sects);
225 }
226 
227 /*
228  * Time to rotate over sectors in a track with track_sects sectors.
229  */
230 static sim_time_t chs_sect_time(struct chs_conf *conf, unsigned track_sects,
231  unsigned sectors)
232 {
233  /* rotational delay = sectors / speed */
234  return sectors * 1000000000ULL / (track_sects * conf->cc_rps);
235 }
236 
237 /*
238  * Simulate request processing.
239  */
240 static sim_time_t chs_req(struct chs_dev *dev, enum storage_req_type type,
241  sector_t sector, long count)
242 {
243  struct chs_conf *conf;
244 
245  sim_time_t seek;
246  sim_time_t rotation;
247  sim_time_t xfer;
248 
249  unsigned head;
250  unsigned cylinder;
251  sector_t sector_target;
252  sector_t sector_target0;
253  unsigned armmove;
254  sector_t track_sects;
255  sector_t sector_at;
256  long sects_dist;
257 
258  M0_ASSERT(count >= 0);
259 
260  /*
261  * Request processing consists of:
262  *
263  * - controller command overhead (constant);
264  * - optional seek to the target cylinder;
265  * - optional head switch;
266  * - write settle for write requests;
267  * - rotational delay;
268  * - transfer loop:
269  * * cylinder transfer loop:
270  * . track transfer;
271  * . head switch;
272  * * track-to-track seek;
273  * * write settle for write requests;
274  */
275 
276  conf = dev->cd_conf;
277  chs_sector_to_chs(conf, sector, &head, &cylinder, &sector_target);
278  sector_target0 = sector_target;
279  armmove = abs((int)(cylinder - dev->cd_cylinder));
280 
281  /* Cylinder seek. */
282  if (armmove != 0) {
283  seek =
284  conf->cc_seek_track_to_track +
285  conf->cc_alpha * (armmove - 1) +
286  conf->cc_beta * sqrt(armmove - 1);
287 
288  if (type == SRT_WRITE)
289  seek += conf->cc_write_settle;
290  } else
291  seek = 0;
292 
293  /* Head switch */
294  if (head != dev->cd_head)
295  seek += conf->cc_head_switch;
296 
297  cnt_mod(&dev->cd_seek_time, seek);
298 
299  /* Rotational delay */
300  track_sects = chs_tracks(conf, cylinder);
301 
302  /* sector currently under the head, ignoring skew */
303  sector_at =
304  /* linear speed in tracks per second for this cylinder */
305  (track_sects * conf->cc_rps *
306  /* time since the beginning of simulation in seconds */
307  dev->cd_storage.sd_sim->ss_bolt / 1000000000) % track_sects;
308 
309  sects_dist = sector_at - sector_target;
310  if (sects_dist < 0)
311  sects_dist += track_sects;
312 
313  rotation = chs_sect_time(conf, track_sects, sects_dist);
314  cnt_mod(&dev->cd_rotation_time, rotation);
315 
316  /* Target sector reached. Start transfer. */
317 
318  /* loop over cylinders */
319  for (xfer = 0; cylinder < conf->cc_cylinders; cylinder++) {
320  /* loop over tracks in a cylinder */
321  for (; head < conf->cc_heads; head++) {
322  /* transfer everything there is at the reached track. */
323  sects_dist = track_sects - sector_target;
324  if (sects_dist > count)
325  sects_dist = count;
326  xfer += chs_sect_time(conf, track_sects, sects_dist);
327  count -= sects_dist;
328  sector_target = 0;
329  if (count > 0)
330  xfer += conf->cc_head_switch;
331  else
332  break;
333  }
334  if (count > 0) {
335  xfer += conf->cc_seek_track_to_track;
336  if (type == SRT_WRITE)
337  xfer += conf->cc_write_settle;
338  head = 0;
339  track_sects = chs_tracks(conf, cylinder);
340  } else
341  break;
342  }
343  cnt_mod(&dev->cd_xfer_time, xfer);
344 
346  SLL_TRACE, "D%s: [%4u:%u:%4llu] -> [%4u:%u:%4llu] %10llu "
347  "%llu+%llu+%llu\n",
348  dev->cd_storage.sd_name,
349  dev->cd_cylinder, dev->cd_head, sector_at,
350  cylinder, head, sector_target0, sector,
351  seek, rotation, xfer);
352 
353  M0_ASSERT(count == 0);
354  M0_ASSERT(cylinder < conf->cc_cylinders);
355  M0_ASSERT(head < conf->cc_heads);
356  dev->cd_cylinder = cylinder;
357  dev->cd_head = head;
358 
359  return conf->cc_command_latency + seek + rotation + xfer;
360 }
361 
362 static int chs_req_done(struct sim_callout *call)
363 {
364  struct chs_dev *dev = call->sc_datum;
365 
366  M0_ASSERT(dev->cd_state == CDS_XFER);
367 
368  dev->cd_state = CDS_IDLE;
369  if (dev->cd_storage.sd_end_io != NULL)
370  dev->cd_storage.sd_end_io(&dev->cd_storage);
371  return 0;
372 }
373 
374 static void chs_submit(struct storage_dev *sdev,
375  enum storage_req_type type,
376  sector_t sector, unsigned long count)
377 {
378  sim_time_t reqtime;
379  struct chs_dev *dev = container_of(sdev, struct chs_dev, cd_storage);
380 
381  M0_ASSERT(dev->cd_state == CDS_IDLE);
382 
383  reqtime = chs_req(dev, type, sector, count);
384  sim_timer_rearm(&dev->cd_todo, reqtime, chs_req_done, dev);
385  dev->cd_state = CDS_XFER;
386 }
387 
390 /*
391  * Local variables:
392  * c-indentation-style: "K&R"
393  * c-basic-offset: 8
394  * tab-width: 8
395  * fill-column: 80
396  * scroll-step: 1
397  * End:
398  */
struct chs_conf * cd_conf
Definition: chs.h:78
struct sim_callout cd_todo
Definition: chs.h:83
Definition: sim.h:152
Definition: chs.h:72
#define NULL
Definition: misc.h:38
unsigned cc_heads
Definition: chs.h:44
static void chs_submit(struct storage_dev *dev, enum storage_req_type type, sector_t sector, unsigned long count)
Definition: chs.c:374
M0_INTERNAL void chs_dev_fini(struct chs_dev *dev)
Definition: chs.c:147
static unsigned chs_tracks(struct chs_conf *conf, unsigned cyl)
Definition: chs.c:159
M0_INTERNAL void chs_conf_fini(struct chs_conf *conf)
Definition: chs.c:123
static void full(void)
Definition: base.c:334
M0_INTERNAL void sim_log(struct sim *s, enum sim_log_level level, const char *format,...)
Definition: sim.c:527
Definition: chs.h:41
Definition: conf.py:1
#define container_of(ptr, type, member)
Definition: misc.h:33
static unsigned chs_sector_cylinder(struct chs_conf *conf, sector_t sector)
Definition: chs.c:177
static void chs_sector_to_chs(struct chs_conf *conf, sector_t sector, unsigned *head, unsigned *cylinder, sector_t *sect_in_track)
Definition: chs.c:204
M0_INTERNAL void cnt_init(struct cnt *cnt, struct cnt *parent, const char *format,...)
Definition: cnt.c:44
static sector_t chs_cylinder_sectors(struct chs_conf *conf, unsigned cyl)
Definition: chs.c:168
static m0_bcount_t count
Definition: xcode.c:167
static int head(struct m0_sm *mach)
Definition: sm.c:468
int i
Definition: dir.c:1033
static sim_time_t chs_sect_time(struct chs_conf *conf, unsigned track_sects, unsigned sectors)
Definition: chs.c:230
unsigned long long sector_t
Definition: storage.h:34
struct cnt cd_write_size
Definition: chs.h:89
M0_INTERNAL void cnt_mod(struct cnt *cnt, cnt_t val)
Definition: cnt.c:92
const char * name
Definition: trace.c:110
M0_INTERNAL void chs_conf_init(struct chs_conf *conf)
Definition: chs.c:43
struct cnt cd_read_size
Definition: chs.h:88
#define M0_ASSERT(cond)
struct crate_conf * conf
char * sd_name
Definition: storage.h:59
storage_req_type
Definition: storage.h:42
sim_time_t ss_bolt
Definition: sim.h:158
static sim_time_t chs_req(struct chs_dev *dev, enum storage_req_type type, sector_t sector, long count)
Definition: chs.c:240
unsigned long long sim_time_t
Definition: sim.h:111
unsigned cd_cylinder
Definition: chs.h:81
Definition: chs.h:76
struct sim * sd_sim
Definition: storage.h:54
struct cnt cd_xfer_time
Definition: chs.h:87
void * sc_datum
Definition: sim.h:138
struct sim * sc_sim
Definition: sim.h:142
Definition: chs.h:73
storage_end_io_t sd_end_io
Definition: storage.h:56
unsigned cd_head
Definition: chs.h:80
M0_INTERNAL void chs_dev_init(struct chs_dev *dev, struct sim *sim, struct chs_conf *conf)
Definition: chs.c:129
static int chs_req_done(struct sim_callout *call)
Definition: chs.c:362
M0_INTERNAL void sim_timer_rearm(struct sim_callout *call, sim_time_t delta, sim_call_t *cfunc, void *datum)
Definition: sim.c:202
struct cnt cd_rotation_time
Definition: chs.h:86
long long int llabs(long long int j)
struct storage_dev cd_storage
Definition: chs.h:77
struct cnt cd_seek_time
Definition: chs.h:85
int type
Definition: dir.c:1031
enum chs_dev_state cd_state
Definition: chs.h:79
unsigned cc_cylinders
Definition: chs.h:45
struct storage_conf * sd_conf
Definition: storage.h:55
Definition: sim.h:287
M0_INTERNAL void cnt_fini(struct cnt *cnt)
Definition: cnt.c:83
storage_submit_t sd_submit
Definition: storage.h:57