xref: /illumos-gate/usr/src/lib/libc/port/gen/memalign.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #pragma weak memalign = _memalign
34 
35 #include "synonyms.h"
36 #include "mallint.h"
37 #include "mtlib.h"
38 
39 #define	_misaligned(p)		((unsigned)(p) & 3)
40 		/* 4-byte "word" alignment is considered ok in LP64 */
41 #define	_nextblk(p, size)	((TREE *)((uintptr_t)(p) + (size)))
42 
43 /*
44  * memalign(align, nbytes)
45  *
46  * Description:
47  *	Returns a block of specified size on a specified alignment boundary.
48  *
49  * Algorithm:
50  *	Malloc enough to ensure that a block can be aligned correctly.
51  *	Find the alignment point and return the fragments
52  *	before and after the block.
53  *
54  * Errors:
55  *	Returns NULL and sets errno as follows:
56  *	[EINVAL]
57  *		if nbytes = 0,
58  *		or if alignment is misaligned,
59  *		or if the heap has been detectably corrupted.
60  *	[ENOMEM]
61  *		if the requested memory could not be allocated.
62  */
63 
64 void *
65 memalign(size_t align, size_t nbytes)
66 {
67 	size_t	 reqsize;	/* Num of bytes to get from malloc() */
68 	TREE	*p;		/* Ptr returned from malloc() */
69 	TREE	*blk;		/* For addressing fragment blocks */
70 	size_t	blksize;	/* Current (shrinking) block size */
71 	TREE	*alignedp;	/* Ptr to properly aligned boundary */
72 	TREE	*aligned_blk;	/* The block to be returned */
73 	size_t	frag_size;	/* size of fragments fore and aft */
74 	size_t	 x;
75 
76 	/*
77 	 * check for valid size and alignment parameters
78 	 * MAX_ALIGN check prevents overflow in later calculation.
79 	 */
80 	if (nbytes == 0 || _misaligned(align) || align == 0 ||
81 	    align > MAX_ALIGN) {
82 		errno = EINVAL;
83 		return (NULL);
84 	}
85 
86 	/*
87 	 * Malloc enough memory to guarantee that the result can be
88 	 * aligned correctly. The worst case is when malloc returns
89 	 * a block so close to the next alignment boundary that a
90 	 * fragment of minimum size cannot be created.  In order to
91 	 * make sure we can handle this, we need to force the
92 	 * alignment to be at least as large as the minimum frag size
93 	 * (MINSIZE + WORDSIZE).
94 	 */
95 
96 	/* check for size that could overflow calculations */
97 	if (nbytes > MAX_MALLOC) {
98 		errno = ENOMEM;
99 		return (NULL);
100 	}
101 	ROUND(nbytes);
102 	if (nbytes < MINSIZE)
103 		nbytes = MINSIZE;
104 	ROUND(align);
105 	while (align < MINSIZE + WORDSIZE)
106 		align <<= 1;
107 	reqsize = nbytes + align + (MINSIZE + WORDSIZE);
108 
109 	/* check for overflow */
110 	if (reqsize < nbytes) {
111 		errno = ENOMEM;
112 		return (NULL);
113 	}
114 
115 	p = (TREE *)malloc(reqsize);
116 	if (p == (TREE *)NULL) {
117 		/* malloc sets errno */
118 		return (NULL);
119 	}
120 	lmutex_lock(&libc_malloc_lock);
121 
122 	/*
123 	 * get size of the entire block (overhead and all)
124 	 */
125 	blk = BLOCK(p);			/* back up to get length word */
126 	blksize = SIZE(blk);
127 	CLRBITS01(blksize);
128 
129 	/*
130 	 * locate the proper alignment boundary within the block.
131 	 */
132 	x = (size_t)p;
133 	if (x % align != 0)
134 		x += align - (x % align);
135 	alignedp = (TREE *)x;
136 	aligned_blk = BLOCK(alignedp);
137 
138 	/*
139 	 * Check out the space to the left of the alignment
140 	 * boundary, and split off a fragment if necessary.
141 	 */
142 	frag_size = (size_t)aligned_blk - (size_t)blk;
143 	if (frag_size != 0) {
144 		/*
145 		 * Create a fragment to the left of the aligned block.
146 		 */
147 		if (frag_size < MINSIZE + WORDSIZE) {
148 			/*
149 			 * Not enough space. So make the split
150 			 * at the other end of the alignment unit.
151 			 * We know this yields enough space, because
152 			 * we forced align >= MINSIZE + WORDSIZE above.
153 			 */
154 			frag_size += align;
155 			aligned_blk = _nextblk(aligned_blk, align);
156 		}
157 		blksize -= frag_size;
158 		SIZE(aligned_blk) = blksize | BIT0;
159 		frag_size -= WORDSIZE;
160 		SIZE(blk) = frag_size | BIT0 | ISBIT1(SIZE(blk));
161 		_free_unlocked(DATA(blk));
162 	}
163 
164 	/*
165 	 * Is there a (sufficiently large) fragment to the
166 	 * right of the aligned block?
167 	 */
168 	frag_size = blksize - nbytes;
169 	if (frag_size >= MINSIZE + WORDSIZE) {
170 		/*
171 		 * split and free a fragment on the right
172 		 */
173 		blksize = SIZE(aligned_blk);
174 		SIZE(aligned_blk) = nbytes;
175 		blk = NEXT(aligned_blk);
176 		SETOLD01(SIZE(aligned_blk), blksize);
177 		frag_size -= WORDSIZE;
178 		SIZE(blk) = frag_size | BIT0;
179 		_free_unlocked(DATA(blk));
180 	}
181 	lmutex_unlock(&libc_malloc_lock);
182 	return (DATA(aligned_blk));
183 }
184