Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
tcm-sita.c
Go to the documentation of this file.
1 /*
2  * tcm-sita.c
3  *
4  * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
5  *
6  * Authors: Ravi Ramachandra <[email protected]>,
7  * Lajos Molnar <[email protected]>
8  *
9  * Copyright (C) 2009-2010 Texas Instruments, Inc.
10  *
11  * This package is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  *
15  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  */
20 #include <linux/slab.h>
21 #include <linux/spinlock.h>
22 
23 #include "tcm-sita.h"
24 
25 #define ALIGN_DOWN(value, align) ((value) & ~((align) - 1))
26 
27 /* Individual selection criteria for different scan areas */
28 static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
29 static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
30 
31 /*********************************************
32  * TCM API - Sita Implementation
33  *********************************************/
34 static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
35  struct tcm_area *area);
36 static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
37 static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
38 static void sita_deinit(struct tcm *tcm);
39 
40 /*********************************************
41  * Main Scanner functions
42  *********************************************/
43 static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
44  struct tcm_area *area);
45 
46 static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
47  struct tcm_area *field, struct tcm_area *area);
48 
49 static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
50  struct tcm_area *field, struct tcm_area *area);
51 
52 static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
53  struct tcm_area *field, struct tcm_area *area);
54 
55 /*********************************************
56  * Support Infrastructure Methods
57  *********************************************/
58 static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
59 
60 static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
61  struct tcm_area *field, s32 criteria,
62  struct score *best);
63 
64 static void get_nearness_factor(struct tcm_area *field,
65  struct tcm_area *candidate,
66  struct nearness_factor *nf);
67 
68 static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
69  struct neighbor_stats *stat);
70 
71 static void fill_area(struct tcm *tcm,
72  struct tcm_area *area, struct tcm_area *parent);
73 
74 
75 /*********************************************/
76 
77 /*********************************************
78  * Utility Methods
79  *********************************************/
80 struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
81 {
82  struct tcm *tcm;
83  struct sita_pvt *pvt;
84  struct tcm_area area = {0};
85  s32 i;
86 
87  if (width == 0 || height == 0)
88  return NULL;
89 
90  tcm = kmalloc(sizeof(*tcm), GFP_KERNEL);
91  pvt = kmalloc(sizeof(*pvt), GFP_KERNEL);
92  if (!tcm || !pvt)
93  goto error;
94 
95  memset(tcm, 0, sizeof(*tcm));
96  memset(pvt, 0, sizeof(*pvt));
97 
98  /* Updating the pointers to SiTA implementation APIs */
99  tcm->height = height;
100  tcm->width = width;
101  tcm->reserve_2d = sita_reserve_2d;
102  tcm->reserve_1d = sita_reserve_1d;
103  tcm->free = sita_free;
104  tcm->deinit = sita_deinit;
105  tcm->pvt = (void *)pvt;
106 
107  spin_lock_init(&(pvt->lock));
108 
109  /* Creating tam map */
110  pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
111  if (!pvt->map)
112  goto error;
113 
114  for (i = 0; i < tcm->width; i++) {
115  pvt->map[i] =
116  kmalloc(sizeof(**pvt->map) * tcm->height,
117  GFP_KERNEL);
118  if (pvt->map[i] == NULL) {
119  while (i--)
120  kfree(pvt->map[i]);
121  kfree(pvt->map);
122  goto error;
123  }
124  }
125 
126  if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
127  pvt->div_pt.x = attr->x;
128  pvt->div_pt.y = attr->y;
129 
130  } else {
131  /* Defaulting to 3:1 ratio on width for 2D area split */
132  /* Defaulting to 3:1 ratio on height for 2D and 1D split */
133  pvt->div_pt.x = (tcm->width * 3) / 4;
134  pvt->div_pt.y = (tcm->height * 3) / 4;
135  }
136 
137  spin_lock(&(pvt->lock));
138  assign(&area, 0, 0, width - 1, height - 1);
139  fill_area(tcm, &area, NULL);
140  spin_unlock(&(pvt->lock));
141  return tcm;
142 
143 error:
144  kfree(tcm);
145  kfree(pvt);
146  return NULL;
147 }
148 
149 static void sita_deinit(struct tcm *tcm)
150 {
151  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
152  struct tcm_area area = {0};
153  s32 i;
154 
155  area.p1.x = tcm->width - 1;
156  area.p1.y = tcm->height - 1;
157 
158  spin_lock(&(pvt->lock));
159  fill_area(tcm, &area, NULL);
160  spin_unlock(&(pvt->lock));
161 
162  for (i = 0; i < tcm->height; i++)
163  kfree(pvt->map[i]);
164  kfree(pvt->map);
165  kfree(pvt);
166 }
167 
177 static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
178  struct tcm_area *area)
179 {
180  s32 ret;
181  struct tcm_area field = {0};
182  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
183 
184  spin_lock(&(pvt->lock));
185 
186  /* Scanning entire container */
187  assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
188 
189  ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
190  if (!ret)
191  /* update map */
192  fill_area(tcm, area, area);
193 
194  spin_unlock(&(pvt->lock));
195  return ret;
196 }
197 
208 static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
209  struct tcm_area *area)
210 {
211  s32 ret;
212  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
213 
214  /* not supporting more than 64 as alignment */
215  if (align > 64)
216  return -EINVAL;
217 
218  /* we prefer 1, 32 and 64 as alignment */
219  align = align <= 1 ? 1 : align <= 32 ? 32 : 64;
220 
221  spin_lock(&(pvt->lock));
222  ret = scan_areas_and_find_fit(tcm, w, h, align, area);
223  if (!ret)
224  /* update map */
225  fill_area(tcm, area, area);
226 
227  spin_unlock(&(pvt->lock));
228  return ret;
229 }
230 
236 static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
237 {
238  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
239 
240  spin_lock(&(pvt->lock));
241 
242  /* check that this is in fact an existing area */
243  WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
244  pvt->map[area->p1.x][area->p1.y] != area);
245 
246  /* Clear the contents of the associated tiles in the map */
247  fill_area(tcm, area, NULL);
248 
249  spin_unlock(&(pvt->lock));
250 
251  return 0;
252 }
253 
274 static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
275  struct tcm_area *field, struct tcm_area *area)
276 {
277  s32 x, y;
278  s16 start_x, end_x, start_y, end_y, found_x = -1;
279  struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
280  struct score best = {{0}, {0}, {0}, 0};
281 
282  start_x = field->p0.x;
283  end_x = field->p1.x;
284  start_y = field->p0.y;
285  end_y = field->p1.y;
286 
287  /* check scan area co-ordinates */
288  if (field->p0.x < field->p1.x ||
289  field->p1.y < field->p0.y)
290  return -EINVAL;
291 
292  /* check if allocation would fit in scan area */
293  if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
294  return -ENOSPC;
295 
296  /* adjust start_x and end_y, as allocation would not fit beyond */
297  start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
298  end_y = end_y - h + 1;
299 
300  /* check if allocation would still fit in scan area */
301  if (start_x < end_x)
302  return -ENOSPC;
303 
304  /* scan field top-to-bottom, right-to-left */
305  for (y = start_y; y <= end_y; y++) {
306  for (x = start_x; x >= end_x; x -= align) {
307  if (is_area_free(map, x, y, w, h)) {
308  found_x = x;
309 
310  /* update best candidate */
311  if (update_candidate(tcm, x, y, w, h, field,
312  CR_R2L_T2B, &best))
313  goto done;
314 
315  /* change upper x bound */
316  end_x = x + 1;
317  break;
318  } else if (map[x][y] && map[x][y]->is2d) {
319  /* step over 2D areas */
320  x = ALIGN(map[x][y]->p0.x - w + 1, align);
321  }
322  }
323 
324  /* break if you find a free area shouldering the scan field */
325  if (found_x == start_x)
326  break;
327  }
328 
329  if (!best.a.tcm)
330  return -ENOSPC;
331 done:
332  assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
333  return 0;
334 }
335 
348 static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
349  struct tcm_area *field, struct tcm_area *area)
350 {
351  s32 x, y;
352  s16 start_x, end_x, start_y, end_y, found_x = -1;
353  struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
354  struct score best = {{0}, {0}, {0}, 0};
355 
356  start_x = field->p0.x;
357  end_x = field->p1.x;
358  start_y = field->p0.y;
359  end_y = field->p1.y;
360 
361  /* check scan area co-ordinates */
362  if (field->p1.x < field->p0.x ||
363  field->p1.y < field->p0.y)
364  return -EINVAL;
365 
366  /* check if allocation would fit in scan area */
367  if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
368  return -ENOSPC;
369 
370  start_x = ALIGN(start_x, align);
371 
372  /* check if allocation would still fit in scan area */
373  if (w > LEN(end_x, start_x))
374  return -ENOSPC;
375 
376  /* adjust end_x and end_y, as allocation would not fit beyond */
377  end_x = end_x - w + 1; /* + 1 to be inclusive */
378  end_y = end_y - h + 1;
379 
380  /* scan field top-to-bottom, left-to-right */
381  for (y = start_y; y <= end_y; y++) {
382  for (x = start_x; x <= end_x; x += align) {
383  if (is_area_free(map, x, y, w, h)) {
384  found_x = x;
385 
386  /* update best candidate */
387  if (update_candidate(tcm, x, y, w, h, field,
388  CR_L2R_T2B, &best))
389  goto done;
390  /* change upper x bound */
391  end_x = x - 1;
392 
393  break;
394  } else if (map[x][y] && map[x][y]->is2d) {
395  /* step over 2D areas */
396  x = ALIGN_DOWN(map[x][y]->p1.x, align);
397  }
398  }
399 
400  /* break if you find a free area shouldering the scan field */
401  if (found_x == start_x)
402  break;
403  }
404 
405  if (!best.a.tcm)
406  return -ENOSPC;
407 done:
408  assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
409  return 0;
410 }
411 
424 static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
425  struct tcm_area *field, struct tcm_area *area)
426 {
427  s32 found = 0;
428  s16 x, y;
429  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
430  struct tcm_area *p;
431 
432  /* check scan area co-ordinates */
433  if (field->p0.y < field->p1.y)
434  return -EINVAL;
435 
440  if (tcm->width != field->p0.x - field->p1.x + 1)
441  return -EINVAL;
442 
443  /* check if allocation would fit in scan area */
444  if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y))
445  return -ENOSPC;
446 
447  x = field->p0.x;
448  y = field->p0.y;
449 
450  /* find num_slots consecutive free slots to the left */
451  while (found < num_slots) {
452  if (y < 0)
453  return -ENOSPC;
454 
455  /* remember bottom-right corner */
456  if (found == 0) {
457  area->p1.x = x;
458  area->p1.y = y;
459  }
460 
461  /* skip busy regions */
462  p = pvt->map[x][y];
463  if (p) {
464  /* move to left of 2D areas, top left of 1D */
465  x = p->p0.x;
466  if (!p->is2d)
467  y = p->p0.y;
468 
469  /* start over */
470  found = 0;
471  } else {
472  /* count consecutive free slots */
473  found++;
474  if (found == num_slots)
475  break;
476  }
477 
478  /* move to the left */
479  if (x == 0)
480  y--;
481  x = (x ? : tcm->width) - 1;
482 
483  }
484 
485  /* set top-left corner */
486  area->p0.x = x;
487  area->p0.y = y;
488  return 0;
489 }
490 
502 static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
503  struct tcm_area *area)
504 {
505  s32 ret = 0;
506  struct tcm_area field = {0};
507  u16 boundary_x, boundary_y;
508  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
509 
510  if (align > 1) {
511  /* prefer top-left corner */
512  boundary_x = pvt->div_pt.x - 1;
513  boundary_y = pvt->div_pt.y - 1;
514 
515  /* expand width and height if needed */
516  if (w > pvt->div_pt.x)
517  boundary_x = tcm->width - 1;
518  if (h > pvt->div_pt.y)
519  boundary_y = tcm->height - 1;
520 
521  assign(&field, 0, 0, boundary_x, boundary_y);
522  ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
523 
524  /* scan whole container if failed, but do not scan 2x */
525  if (ret != 0 && (boundary_x != tcm->width - 1 ||
526  boundary_y != tcm->height - 1)) {
527  /* scan the entire container if nothing found */
528  assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
529  ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
530  }
531  } else if (align == 1) {
532  /* prefer top-right corner */
533  boundary_x = pvt->div_pt.x;
534  boundary_y = pvt->div_pt.y - 1;
535 
536  /* expand width and height if needed */
537  if (w > (tcm->width - pvt->div_pt.x))
538  boundary_x = 0;
539  if (h > pvt->div_pt.y)
540  boundary_y = tcm->height - 1;
541 
542  assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
543  ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
544 
545  /* scan whole container if failed, but do not scan 2x */
546  if (ret != 0 && (boundary_x != 0 ||
547  boundary_y != tcm->height - 1)) {
548  /* scan the entire container if nothing found */
549  assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
550  ret = scan_r2l_t2b(tcm, w, h, align, &field,
551  area);
552  }
553  }
554 
555  return ret;
556 }
557 
558 /* check if an entire area is free */
559 static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h)
560 {
561  u16 x = 0, y = 0;
562  for (y = y0; y < y0 + h; y++) {
563  for (x = x0; x < x0 + w; x++) {
564  if (map[x][y])
565  return false;
566  }
567  }
568  return true;
569 }
570 
571 /* fills an area with a parent tcm_area */
572 static void fill_area(struct tcm *tcm, struct tcm_area *area,
573  struct tcm_area *parent)
574 {
575  s32 x, y;
576  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
577  struct tcm_area a, a_;
578 
579  /* set area's tcm; otherwise, enumerator considers it invalid */
580  area->tcm = tcm;
581 
582  tcm_for_each_slice(a, *area, a_) {
583  for (x = a.p0.x; x <= a.p1.x; ++x)
584  for (y = a.p0.y; y <= a.p1.y; ++y)
585  pvt->map[x][y] = parent;
586 
587  }
588 }
589 
602 static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
603  struct tcm_area *field, s32 criteria,
604  struct score *best)
605 {
606  struct score me; /* score for area */
607 
608  /*
609  * NOTE: For horizontal bias we always give the first found, because our
610  * scan is horizontal-raster-based and the first candidate will always
611  * have the horizontal bias.
612  */
613  bool first = criteria & CR_BIAS_HORIZONTAL;
614 
615  assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
616 
617  /* calculate score for current candidate */
618  if (!first) {
619  get_neighbor_stats(tcm, &me.a, &me.n);
620  me.neighs = me.n.edge + me.n.busy;
621  get_nearness_factor(field, &me.a, &me.f);
622  }
623 
624  /* the 1st candidate is always the best */
625  if (!best->a.tcm)
626  goto better;
627 
628  BUG_ON(first);
629 
630  /* diagonal balance check */
631  if ((criteria & CR_DIAGONAL_BALANCE) &&
632  best->neighs <= me.neighs &&
633  (best->neighs < me.neighs ||
634  /* this implies that neighs and occupied match */
635  best->n.busy < me.n.busy ||
636  (best->n.busy == me.n.busy &&
637  /* check the nearness factor */
638  best->f.x + best->f.y > me.f.x + me.f.y)))
639  goto better;
640 
641  /* not better, keep going */
642  return 0;
643 
644 better:
645  /* save current area as best */
646  memcpy(best, &me, sizeof(me));
647  best->a.tcm = tcm;
648  return first;
649 }
650 
655 static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
656  struct nearness_factor *nf)
657 {
662  nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
663  (field->p1.x - field->p0.x);
664  nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
665  (field->p1.y - field->p0.y);
666 }
667 
668 /* get neighbor statistics */
669 static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
670  struct neighbor_stats *stat)
671 {
672  s16 x = 0, y = 0;
673  struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
674 
675  /* Clearing any exisiting values */
676  memset(stat, 0, sizeof(*stat));
677 
678  /* process top & bottom edges */
679  for (x = area->p0.x; x <= area->p1.x; x++) {
680  if (area->p0.y == 0)
681  stat->edge++;
682  else if (pvt->map[x][area->p0.y - 1])
683  stat->busy++;
684 
685  if (area->p1.y == tcm->height - 1)
686  stat->edge++;
687  else if (pvt->map[x][area->p1.y + 1])
688  stat->busy++;
689  }
690 
691  /* process left & right edges */
692  for (y = area->p0.y; y <= area->p1.y; ++y) {
693  if (area->p0.x == 0)
694  stat->edge++;
695  else if (pvt->map[area->p0.x - 1][y])
696  stat->busy++;
697 
698  if (area->p1.x == tcm->width - 1)
699  stat->edge++;
700  else if (pvt->map[area->p1.x + 1][y])
701  stat->busy++;
702  }
703 }