1*753ac610SCharles Manning /*
2*753ac610SCharles Manning * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3*753ac610SCharles Manning *
4*753ac610SCharles Manning * Copyright (C) 2002-2011 Aleph One Ltd.
5*753ac610SCharles Manning * for Toby Churchill Ltd and Brightstar Engineering
6*753ac610SCharles Manning *
7*753ac610SCharles Manning * Created by Charles Manning <charles@aleph1.co.uk>
8*753ac610SCharles Manning *
9*753ac610SCharles Manning * This program is free software; you can redistribute it and/or modify
10*753ac610SCharles Manning * it under the terms of the GNU General Public License version 2 as
11*753ac610SCharles Manning * published by the Free Software Foundation.
12*753ac610SCharles Manning */
13*753ac610SCharles Manning
14*753ac610SCharles Manning #include "yaffs_allocator.h"
15*753ac610SCharles Manning #include "yaffs_guts.h"
16*753ac610SCharles Manning #include "yaffs_trace.h"
17*753ac610SCharles Manning #include "yportenv.h"
18*753ac610SCharles Manning
19*753ac610SCharles Manning /*
20*753ac610SCharles Manning * Each entry in yaffs_tnode_list and yaffs_obj_list hold blocks
21*753ac610SCharles Manning * of approx 100 objects that are themn allocated singly.
22*753ac610SCharles Manning * This is basically a simplified slab allocator.
23*753ac610SCharles Manning *
24*753ac610SCharles Manning * We don't use the Linux slab allocator because slab does not allow
25*753ac610SCharles Manning * us to dump all the objects in one hit when we do a umount and tear
26*753ac610SCharles Manning * down all the tnodes and objects. slab requires that we first free
27*753ac610SCharles Manning * the individual objects.
28*753ac610SCharles Manning *
29*753ac610SCharles Manning * Once yaffs has been mainlined I shall try to motivate for a change
30*753ac610SCharles Manning * to slab to provide the extra features we need here.
31*753ac610SCharles Manning */
32*753ac610SCharles Manning
33*753ac610SCharles Manning struct yaffs_tnode_list {
34*753ac610SCharles Manning struct yaffs_tnode_list *next;
35*753ac610SCharles Manning struct yaffs_tnode *tnodes;
36*753ac610SCharles Manning };
37*753ac610SCharles Manning
38*753ac610SCharles Manning struct yaffs_obj_list {
39*753ac610SCharles Manning struct yaffs_obj_list *next;
40*753ac610SCharles Manning struct yaffs_obj *objects;
41*753ac610SCharles Manning };
42*753ac610SCharles Manning
43*753ac610SCharles Manning struct yaffs_allocator {
44*753ac610SCharles Manning int n_tnodes_created;
45*753ac610SCharles Manning struct yaffs_tnode *free_tnodes;
46*753ac610SCharles Manning int n_free_tnodes;
47*753ac610SCharles Manning struct yaffs_tnode_list *alloc_tnode_list;
48*753ac610SCharles Manning
49*753ac610SCharles Manning int n_obj_created;
50*753ac610SCharles Manning struct list_head free_objs;
51*753ac610SCharles Manning int n_free_objects;
52*753ac610SCharles Manning
53*753ac610SCharles Manning struct yaffs_obj_list *allocated_obj_list;
54*753ac610SCharles Manning };
55*753ac610SCharles Manning
yaffs_deinit_raw_tnodes(struct yaffs_dev * dev)56*753ac610SCharles Manning static void yaffs_deinit_raw_tnodes(struct yaffs_dev *dev)
57*753ac610SCharles Manning {
58*753ac610SCharles Manning struct yaffs_allocator *allocator =
59*753ac610SCharles Manning (struct yaffs_allocator *)dev->allocator;
60*753ac610SCharles Manning struct yaffs_tnode_list *tmp;
61*753ac610SCharles Manning
62*753ac610SCharles Manning if (!allocator) {
63*753ac610SCharles Manning BUG();
64*753ac610SCharles Manning return;
65*753ac610SCharles Manning }
66*753ac610SCharles Manning
67*753ac610SCharles Manning while (allocator->alloc_tnode_list) {
68*753ac610SCharles Manning tmp = allocator->alloc_tnode_list->next;
69*753ac610SCharles Manning
70*753ac610SCharles Manning kfree(allocator->alloc_tnode_list->tnodes);
71*753ac610SCharles Manning kfree(allocator->alloc_tnode_list);
72*753ac610SCharles Manning allocator->alloc_tnode_list = tmp;
73*753ac610SCharles Manning }
74*753ac610SCharles Manning
75*753ac610SCharles Manning allocator->free_tnodes = NULL;
76*753ac610SCharles Manning allocator->n_free_tnodes = 0;
77*753ac610SCharles Manning allocator->n_tnodes_created = 0;
78*753ac610SCharles Manning }
79*753ac610SCharles Manning
yaffs_init_raw_tnodes(struct yaffs_dev * dev)80*753ac610SCharles Manning static void yaffs_init_raw_tnodes(struct yaffs_dev *dev)
81*753ac610SCharles Manning {
82*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
83*753ac610SCharles Manning
84*753ac610SCharles Manning if (!allocator) {
85*753ac610SCharles Manning BUG();
86*753ac610SCharles Manning return;
87*753ac610SCharles Manning }
88*753ac610SCharles Manning
89*753ac610SCharles Manning allocator->alloc_tnode_list = NULL;
90*753ac610SCharles Manning allocator->free_tnodes = NULL;
91*753ac610SCharles Manning allocator->n_free_tnodes = 0;
92*753ac610SCharles Manning allocator->n_tnodes_created = 0;
93*753ac610SCharles Manning }
94*753ac610SCharles Manning
yaffs_create_tnodes(struct yaffs_dev * dev,int n_tnodes)95*753ac610SCharles Manning static int yaffs_create_tnodes(struct yaffs_dev *dev, int n_tnodes)
96*753ac610SCharles Manning {
97*753ac610SCharles Manning struct yaffs_allocator *allocator =
98*753ac610SCharles Manning (struct yaffs_allocator *)dev->allocator;
99*753ac610SCharles Manning int i;
100*753ac610SCharles Manning struct yaffs_tnode *new_tnodes;
101*753ac610SCharles Manning u8 *mem;
102*753ac610SCharles Manning struct yaffs_tnode *curr;
103*753ac610SCharles Manning struct yaffs_tnode *next;
104*753ac610SCharles Manning struct yaffs_tnode_list *tnl;
105*753ac610SCharles Manning
106*753ac610SCharles Manning if (!allocator) {
107*753ac610SCharles Manning BUG();
108*753ac610SCharles Manning return YAFFS_FAIL;
109*753ac610SCharles Manning }
110*753ac610SCharles Manning
111*753ac610SCharles Manning if (n_tnodes < 1)
112*753ac610SCharles Manning return YAFFS_OK;
113*753ac610SCharles Manning
114*753ac610SCharles Manning /* make these things */
115*753ac610SCharles Manning new_tnodes = kmalloc(n_tnodes * dev->tnode_size, GFP_NOFS);
116*753ac610SCharles Manning mem = (u8 *) new_tnodes;
117*753ac610SCharles Manning
118*753ac610SCharles Manning if (!new_tnodes) {
119*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR,
120*753ac610SCharles Manning "yaffs: Could not allocate Tnodes");
121*753ac610SCharles Manning return YAFFS_FAIL;
122*753ac610SCharles Manning }
123*753ac610SCharles Manning
124*753ac610SCharles Manning /* New hookup for wide tnodes */
125*753ac610SCharles Manning for (i = 0; i < n_tnodes - 1; i++) {
126*753ac610SCharles Manning curr = (struct yaffs_tnode *)&mem[i * dev->tnode_size];
127*753ac610SCharles Manning next = (struct yaffs_tnode *)&mem[(i + 1) * dev->tnode_size];
128*753ac610SCharles Manning curr->internal[0] = next;
129*753ac610SCharles Manning }
130*753ac610SCharles Manning
131*753ac610SCharles Manning curr = (struct yaffs_tnode *)&mem[(n_tnodes - 1) * dev->tnode_size];
132*753ac610SCharles Manning curr->internal[0] = allocator->free_tnodes;
133*753ac610SCharles Manning allocator->free_tnodes = (struct yaffs_tnode *)mem;
134*753ac610SCharles Manning
135*753ac610SCharles Manning allocator->n_free_tnodes += n_tnodes;
136*753ac610SCharles Manning allocator->n_tnodes_created += n_tnodes;
137*753ac610SCharles Manning
138*753ac610SCharles Manning /* Now add this bunch of tnodes to a list for freeing up.
139*753ac610SCharles Manning * NB If we can't add this to the management list it isn't fatal
140*753ac610SCharles Manning * but it just means we can't free this bunch of tnodes later.
141*753ac610SCharles Manning */
142*753ac610SCharles Manning tnl = kmalloc(sizeof(struct yaffs_tnode_list), GFP_NOFS);
143*753ac610SCharles Manning if (!tnl) {
144*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ERROR,
145*753ac610SCharles Manning "Could not add tnodes to management list");
146*753ac610SCharles Manning return YAFFS_FAIL;
147*753ac610SCharles Manning } else {
148*753ac610SCharles Manning tnl->tnodes = new_tnodes;
149*753ac610SCharles Manning tnl->next = allocator->alloc_tnode_list;
150*753ac610SCharles Manning allocator->alloc_tnode_list = tnl;
151*753ac610SCharles Manning }
152*753ac610SCharles Manning
153*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ALLOCATE, "Tnodes added");
154*753ac610SCharles Manning
155*753ac610SCharles Manning return YAFFS_OK;
156*753ac610SCharles Manning }
157*753ac610SCharles Manning
yaffs_alloc_raw_tnode(struct yaffs_dev * dev)158*753ac610SCharles Manning struct yaffs_tnode *yaffs_alloc_raw_tnode(struct yaffs_dev *dev)
159*753ac610SCharles Manning {
160*753ac610SCharles Manning struct yaffs_allocator *allocator =
161*753ac610SCharles Manning (struct yaffs_allocator *)dev->allocator;
162*753ac610SCharles Manning struct yaffs_tnode *tn = NULL;
163*753ac610SCharles Manning
164*753ac610SCharles Manning if (!allocator) {
165*753ac610SCharles Manning BUG();
166*753ac610SCharles Manning return NULL;
167*753ac610SCharles Manning }
168*753ac610SCharles Manning
169*753ac610SCharles Manning /* If there are none left make more */
170*753ac610SCharles Manning if (!allocator->free_tnodes)
171*753ac610SCharles Manning yaffs_create_tnodes(dev, YAFFS_ALLOCATION_NTNODES);
172*753ac610SCharles Manning
173*753ac610SCharles Manning if (allocator->free_tnodes) {
174*753ac610SCharles Manning tn = allocator->free_tnodes;
175*753ac610SCharles Manning allocator->free_tnodes = allocator->free_tnodes->internal[0];
176*753ac610SCharles Manning allocator->n_free_tnodes--;
177*753ac610SCharles Manning }
178*753ac610SCharles Manning
179*753ac610SCharles Manning return tn;
180*753ac610SCharles Manning }
181*753ac610SCharles Manning
182*753ac610SCharles Manning /* FreeTnode frees up a tnode and puts it back on the free list */
yaffs_free_raw_tnode(struct yaffs_dev * dev,struct yaffs_tnode * tn)183*753ac610SCharles Manning void yaffs_free_raw_tnode(struct yaffs_dev *dev, struct yaffs_tnode *tn)
184*753ac610SCharles Manning {
185*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
186*753ac610SCharles Manning
187*753ac610SCharles Manning if (!allocator) {
188*753ac610SCharles Manning BUG();
189*753ac610SCharles Manning return;
190*753ac610SCharles Manning }
191*753ac610SCharles Manning
192*753ac610SCharles Manning if (tn) {
193*753ac610SCharles Manning tn->internal[0] = allocator->free_tnodes;
194*753ac610SCharles Manning allocator->free_tnodes = tn;
195*753ac610SCharles Manning allocator->n_free_tnodes++;
196*753ac610SCharles Manning }
197*753ac610SCharles Manning dev->checkpoint_blocks_required = 0; /* force recalculation */
198*753ac610SCharles Manning }
199*753ac610SCharles Manning
200*753ac610SCharles Manning /*--------------- yaffs_obj alloaction ------------------------
201*753ac610SCharles Manning *
202*753ac610SCharles Manning * Free yaffs_objs are stored in a list using obj->siblings.
203*753ac610SCharles Manning * The blocks of allocated objects are stored in a linked list.
204*753ac610SCharles Manning */
205*753ac610SCharles Manning
yaffs_init_raw_objs(struct yaffs_dev * dev)206*753ac610SCharles Manning static void yaffs_init_raw_objs(struct yaffs_dev *dev)
207*753ac610SCharles Manning {
208*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
209*753ac610SCharles Manning
210*753ac610SCharles Manning if (!allocator) {
211*753ac610SCharles Manning BUG();
212*753ac610SCharles Manning return;
213*753ac610SCharles Manning }
214*753ac610SCharles Manning
215*753ac610SCharles Manning allocator->allocated_obj_list = NULL;
216*753ac610SCharles Manning INIT_LIST_HEAD(&allocator->free_objs);
217*753ac610SCharles Manning allocator->n_free_objects = 0;
218*753ac610SCharles Manning }
219*753ac610SCharles Manning
yaffs_deinit_raw_objs(struct yaffs_dev * dev)220*753ac610SCharles Manning static void yaffs_deinit_raw_objs(struct yaffs_dev *dev)
221*753ac610SCharles Manning {
222*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
223*753ac610SCharles Manning struct yaffs_obj_list *tmp;
224*753ac610SCharles Manning
225*753ac610SCharles Manning if (!allocator) {
226*753ac610SCharles Manning BUG();
227*753ac610SCharles Manning return;
228*753ac610SCharles Manning }
229*753ac610SCharles Manning
230*753ac610SCharles Manning while (allocator->allocated_obj_list) {
231*753ac610SCharles Manning tmp = allocator->allocated_obj_list->next;
232*753ac610SCharles Manning kfree(allocator->allocated_obj_list->objects);
233*753ac610SCharles Manning kfree(allocator->allocated_obj_list);
234*753ac610SCharles Manning allocator->allocated_obj_list = tmp;
235*753ac610SCharles Manning }
236*753ac610SCharles Manning
237*753ac610SCharles Manning INIT_LIST_HEAD(&allocator->free_objs);
238*753ac610SCharles Manning allocator->n_free_objects = 0;
239*753ac610SCharles Manning allocator->n_obj_created = 0;
240*753ac610SCharles Manning }
241*753ac610SCharles Manning
yaffs_create_free_objs(struct yaffs_dev * dev,int n_obj)242*753ac610SCharles Manning static int yaffs_create_free_objs(struct yaffs_dev *dev, int n_obj)
243*753ac610SCharles Manning {
244*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
245*753ac610SCharles Manning int i;
246*753ac610SCharles Manning struct yaffs_obj *new_objs;
247*753ac610SCharles Manning struct yaffs_obj_list *list;
248*753ac610SCharles Manning
249*753ac610SCharles Manning if (!allocator) {
250*753ac610SCharles Manning BUG();
251*753ac610SCharles Manning return YAFFS_FAIL;
252*753ac610SCharles Manning }
253*753ac610SCharles Manning
254*753ac610SCharles Manning if (n_obj < 1)
255*753ac610SCharles Manning return YAFFS_OK;
256*753ac610SCharles Manning
257*753ac610SCharles Manning /* make these things */
258*753ac610SCharles Manning new_objs = kmalloc(n_obj * sizeof(struct yaffs_obj), GFP_NOFS);
259*753ac610SCharles Manning list = kmalloc(sizeof(struct yaffs_obj_list), GFP_NOFS);
260*753ac610SCharles Manning
261*753ac610SCharles Manning if (!new_objs || !list) {
262*753ac610SCharles Manning kfree(new_objs);
263*753ac610SCharles Manning new_objs = NULL;
264*753ac610SCharles Manning kfree(list);
265*753ac610SCharles Manning list = NULL;
266*753ac610SCharles Manning yaffs_trace(YAFFS_TRACE_ALLOCATE,
267*753ac610SCharles Manning "Could not allocate more objects");
268*753ac610SCharles Manning return YAFFS_FAIL;
269*753ac610SCharles Manning }
270*753ac610SCharles Manning
271*753ac610SCharles Manning /* Hook them into the free list */
272*753ac610SCharles Manning for (i = 0; i < n_obj; i++)
273*753ac610SCharles Manning list_add(&new_objs[i].siblings, &allocator->free_objs);
274*753ac610SCharles Manning
275*753ac610SCharles Manning allocator->n_free_objects += n_obj;
276*753ac610SCharles Manning allocator->n_obj_created += n_obj;
277*753ac610SCharles Manning
278*753ac610SCharles Manning /* Now add this bunch of Objects to a list for freeing up. */
279*753ac610SCharles Manning
280*753ac610SCharles Manning list->objects = new_objs;
281*753ac610SCharles Manning list->next = allocator->allocated_obj_list;
282*753ac610SCharles Manning allocator->allocated_obj_list = list;
283*753ac610SCharles Manning
284*753ac610SCharles Manning return YAFFS_OK;
285*753ac610SCharles Manning }
286*753ac610SCharles Manning
yaffs_alloc_raw_obj(struct yaffs_dev * dev)287*753ac610SCharles Manning struct yaffs_obj *yaffs_alloc_raw_obj(struct yaffs_dev *dev)
288*753ac610SCharles Manning {
289*753ac610SCharles Manning struct yaffs_obj *obj = NULL;
290*753ac610SCharles Manning struct list_head *lh;
291*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
292*753ac610SCharles Manning
293*753ac610SCharles Manning if (!allocator) {
294*753ac610SCharles Manning BUG();
295*753ac610SCharles Manning return obj;
296*753ac610SCharles Manning }
297*753ac610SCharles Manning
298*753ac610SCharles Manning /* If there are none left make more */
299*753ac610SCharles Manning if (list_empty(&allocator->free_objs))
300*753ac610SCharles Manning yaffs_create_free_objs(dev, YAFFS_ALLOCATION_NOBJECTS);
301*753ac610SCharles Manning
302*753ac610SCharles Manning if (!list_empty(&allocator->free_objs)) {
303*753ac610SCharles Manning lh = allocator->free_objs.next;
304*753ac610SCharles Manning obj = list_entry(lh, struct yaffs_obj, siblings);
305*753ac610SCharles Manning list_del_init(lh);
306*753ac610SCharles Manning allocator->n_free_objects--;
307*753ac610SCharles Manning }
308*753ac610SCharles Manning
309*753ac610SCharles Manning return obj;
310*753ac610SCharles Manning }
311*753ac610SCharles Manning
yaffs_free_raw_obj(struct yaffs_dev * dev,struct yaffs_obj * obj)312*753ac610SCharles Manning void yaffs_free_raw_obj(struct yaffs_dev *dev, struct yaffs_obj *obj)
313*753ac610SCharles Manning {
314*753ac610SCharles Manning
315*753ac610SCharles Manning struct yaffs_allocator *allocator = dev->allocator;
316*753ac610SCharles Manning
317*753ac610SCharles Manning if (!allocator) {
318*753ac610SCharles Manning BUG();
319*753ac610SCharles Manning return;
320*753ac610SCharles Manning }
321*753ac610SCharles Manning
322*753ac610SCharles Manning /* Link into the free list. */
323*753ac610SCharles Manning list_add(&obj->siblings, &allocator->free_objs);
324*753ac610SCharles Manning allocator->n_free_objects++;
325*753ac610SCharles Manning }
326*753ac610SCharles Manning
yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev * dev)327*753ac610SCharles Manning void yaffs_deinit_raw_tnodes_and_objs(struct yaffs_dev *dev)
328*753ac610SCharles Manning {
329*753ac610SCharles Manning
330*753ac610SCharles Manning if (!dev->allocator) {
331*753ac610SCharles Manning BUG();
332*753ac610SCharles Manning return;
333*753ac610SCharles Manning }
334*753ac610SCharles Manning
335*753ac610SCharles Manning yaffs_deinit_raw_tnodes(dev);
336*753ac610SCharles Manning yaffs_deinit_raw_objs(dev);
337*753ac610SCharles Manning kfree(dev->allocator);
338*753ac610SCharles Manning dev->allocator = NULL;
339*753ac610SCharles Manning }
340*753ac610SCharles Manning
yaffs_init_raw_tnodes_and_objs(struct yaffs_dev * dev)341*753ac610SCharles Manning void yaffs_init_raw_tnodes_and_objs(struct yaffs_dev *dev)
342*753ac610SCharles Manning {
343*753ac610SCharles Manning struct yaffs_allocator *allocator;
344*753ac610SCharles Manning
345*753ac610SCharles Manning if (dev->allocator) {
346*753ac610SCharles Manning BUG();
347*753ac610SCharles Manning return;
348*753ac610SCharles Manning }
349*753ac610SCharles Manning
350*753ac610SCharles Manning allocator = kmalloc(sizeof(struct yaffs_allocator), GFP_NOFS);
351*753ac610SCharles Manning if (allocator) {
352*753ac610SCharles Manning dev->allocator = allocator;
353*753ac610SCharles Manning yaffs_init_raw_tnodes(dev);
354*753ac610SCharles Manning yaffs_init_raw_objs(dev);
355*753ac610SCharles Manning }
356*753ac610SCharles Manning }
357