[go: nahoru, domu]

1/*
2 * tcm-sita.c
3 *
4 * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm
5 *
6 * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
7 *          Lajos Molnar <molnar@ti.com>
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 */
28static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL;
29static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE;
30
31/*********************************************
32 *	TCM API - Sita Implementation
33 *********************************************/
34static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
35			   struct tcm_area *area);
36static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
37static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
38static void sita_deinit(struct tcm *tcm);
39
40/*********************************************
41 *	Main Scanner functions
42 *********************************************/
43static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
44				   struct tcm_area *area);
45
46static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
47			struct tcm_area *field, struct tcm_area *area);
48
49static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
50			struct tcm_area *field, struct tcm_area *area);
51
52static 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 *********************************************/
58static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
59
60static 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
64static void get_nearness_factor(struct tcm_area *field,
65				struct tcm_area *candidate,
66				struct nearness_factor *nf);
67
68static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
69			       struct neighbor_stats *stat);
70
71static void fill_area(struct tcm *tcm,
72				struct tcm_area *area, struct tcm_area *parent);
73
74
75/*********************************************/
76
77/*********************************************
78 *	Utility Methods
79 *********************************************/
80struct 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
143error:
144	kfree(tcm);
145	kfree(pvt);
146	return NULL;
147}
148
149static 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
168/**
169 * Reserve a 1D area in the container
170 *
171 * @param num_slots	size of 1D area
172 * @param area		pointer to the area that will be populated with the
173 *			reserved area
174 *
175 * @return 0 on success, non-0 error value on failure.
176 */
177static 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
198/**
199 * Reserve a 2D area in the container
200 *
201 * @param w	width
202 * @param h	height
203 * @param area	pointer to the area that will be populated with the reserved
204 *		area
205 *
206 * @return 0 on success, non-0 error value on failure.
207 */
208static 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
231/**
232 * Unreserve a previously allocated 2D or 1D area
233 * @param area	area to be freed
234 * @return 0 - success
235 */
236static 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
254/**
255 * Note: In general the cordinates in the scan field area relevant to the can
256 * sweep directions. The scan origin (e.g. top-left corner) will always be
257 * the p0 member of the field.  Therfore, for a scan from top-left p0.x <= p1.x
258 * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
259 * <= p0.y
260 */
261
262/**
263 * Raster scan horizontally right to left from top to bottom to find a place for
264 * a 2D area of given size inside a scan field.
265 *
266 * @param w	width of desired area
267 * @param h	height of desired area
268 * @param align	desired area alignment
269 * @param area	pointer to the area that will be set to the best position
270 * @param field	area to scan (inclusive)
271 *
272 * @return 0 on success, non-0 error value on failure.
273 */
274static 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;
331done:
332	assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
333	return 0;
334}
335
336/**
337 * Raster scan horizontally left to right from top to bottom to find a place for
338 * a 2D area of given size inside a scan field.
339 *
340 * @param w	width of desired area
341 * @param h	height of desired area
342 * @param align	desired area alignment
343 * @param area	pointer to the area that will be set to the best position
344 * @param field	area to scan (inclusive)
345 *
346 * @return 0 on success, non-0 error value on failure.
347 */
348static 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;
407done:
408	assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
409	return 0;
410}
411
412/**
413 * Raster scan horizontally right to left from bottom to top to find a place
414 * for a 1D area of given size inside a scan field.
415 *
416 * @param num_slots	size of desired area
417 * @param align		desired area alignment
418 * @param area		pointer to the area that will be set to the best
419 *			position
420 * @param field		area to scan (inclusive)
421 *
422 * @return 0 on success, non-0 error value on failure.
423 */
424static 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
436	/**
437	 * Currently we only support full width 1D scan field, which makes sense
438	 * since 1D slot-ordering spans the full container width.
439	 */
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
491/**
492 * Find a place for a 2D area of given size inside a scan field based on its
493 * alignment needs.
494 *
495 * @param w	width of desired area
496 * @param h	height of desired area
497 * @param align	desired area alignment
498 * @param area	pointer to the area that will be set to the best position
499 *
500 * @return 0 on success, non-0 error value on failure.
501 */
502static 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 */
559static 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 */
572static 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
590/**
591 * Compares a candidate area to the current best area, and if it is a better
592 * fit, it updates the best to this one.
593 *
594 * @param x0, y0, w, h		top, left, width, height of candidate area
595 * @param field			scan field
596 * @param criteria		scan criteria
597 * @param best			best candidate and its scores
598 *
599 * @return 1 (true) if the candidate area is known to be the final best, so no
600 * more searching should be performed
601 */
602static 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
644better:
645	/* save current area as best */
646	memcpy(best, &me, sizeof(me));
647	best->a.tcm = tcm;
648	return first;
649}
650
651/**
652 * Calculate the nearness factor of an area in a search field.  The nearness
653 * factor is smaller if the area is closer to the search origin.
654 */
655static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
656				struct nearness_factor *nf)
657{
658	/**
659	 * Using signed math as field coordinates may be reversed if
660	 * search direction is right-to-left or bottom-to-top.
661	 */
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 */
669static 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}
704