Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dmm.c
Go to the documentation of this file.
1 /*
2  * dmm.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * The Dynamic Memory Manager (DMM) module manages the DSP Virtual address
7  * space that can be directly mapped to any MPU buffer or memory region
8  *
9  * Notes:
10  * Region: Generic memory entitiy having a start address and a size
11  * Chunk: Reserved region
12  *
13  * Copyright (C) 2005-2006 Texas Instruments, Inc.
14  *
15  * This package is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License version 2 as
17  * published by the Free Software Foundation.
18  *
19  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
21  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  */
23 #include <linux/types.h>
24 
25 /* ----------------------------------- Host OS */
26 #include <dspbridge/host_os.h>
27 
28 /* ----------------------------------- DSP/BIOS Bridge */
29 #include <dspbridge/dbdefs.h>
30 
31 /* ----------------------------------- OS Adaptation Layer */
32 #include <dspbridge/sync.h>
33 
34 /* ----------------------------------- Platform Manager */
35 #include <dspbridge/dev.h>
36 #include <dspbridge/proc.h>
37 
38 /* ----------------------------------- This */
39 #include <dspbridge/dmm.h>
40 
41 /* ----------------------------------- Defines, Data Structures, Typedefs */
42 #define DMM_ADDR_VIRTUAL(a) \
43  (((struct map_page *)(a) - virtual_mapping_table) * PG_SIZE4K +\
44  dyn_mem_map_beg)
45 #define DMM_ADDR_TO_INDEX(a) (((a) - dyn_mem_map_beg) / PG_SIZE4K)
46 
47 /* DMM Mgr */
48 struct dmm_object {
49  /* Dmm Lock is used to serialize access mem manager for
50  * multi-threads. */
51  spinlock_t dmm_lock; /* Lock to access dmm mgr */
52 };
53 
54 struct map_page {
59 };
60 
61 /* Create the free list */
62 static struct map_page *virtual_mapping_table;
63 static u32 free_region; /* The index of free region */
64 static u32 free_size;
65 static u32 dyn_mem_map_beg; /* The Beginning of dynamic memory mapping */
66 static u32 table_size; /* The size of virt and phys pages tables */
67 
68 /* ----------------------------------- Function Prototypes */
69 static struct map_page *get_region(u32 addr);
70 static struct map_page *get_free_region(u32 len);
71 static struct map_page *get_mapped_region(u32 addrs);
72 
73 /* ======== dmm_create_tables ========
74  * Purpose:
75  * Create table to hold the information of physical address
76  * the buffer pages that is passed by the user, and the table
77  * to hold the information of the virtual memory that is reserved
78  * for DSP.
79  */
80 int dmm_create_tables(struct dmm_object *dmm_mgr, u32 addr, u32 size)
81 {
82  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
83  int status = 0;
84 
85  status = dmm_delete_tables(dmm_obj);
86  if (!status) {
87  dyn_mem_map_beg = addr;
88  table_size = PG_ALIGN_HIGH(size, PG_SIZE4K) / PG_SIZE4K;
89  /* Create the free list */
90  virtual_mapping_table = __vmalloc(table_size *
91  sizeof(struct map_page), GFP_KERNEL |
93  if (virtual_mapping_table == NULL)
94  status = -ENOMEM;
95  else {
96  /* On successful allocation,
97  * all entries are zero ('free') */
98  free_region = 0;
99  free_size = table_size * PG_SIZE4K;
100  virtual_mapping_table[0].region_size = table_size;
101  }
102  }
103 
104  if (status)
105  pr_err("%s: failure, status 0x%x\n", __func__, status);
106 
107  return status;
108 }
109 
110 /*
111  * ======== dmm_create ========
112  * Purpose:
113  * Create a dynamic memory manager object.
114  */
115 int dmm_create(struct dmm_object **dmm_manager,
116  struct dev_object *hdev_obj,
117  const struct dmm_mgrattrs *mgr_attrts)
118 {
119  struct dmm_object *dmm_obj = NULL;
120  int status = 0;
121 
122  *dmm_manager = NULL;
123  /* create, zero, and tag a cmm mgr object */
124  dmm_obj = kzalloc(sizeof(struct dmm_object), GFP_KERNEL);
125  if (dmm_obj != NULL) {
126  spin_lock_init(&dmm_obj->dmm_lock);
127  *dmm_manager = dmm_obj;
128  } else {
129  status = -ENOMEM;
130  }
131 
132  return status;
133 }
134 
135 /*
136  * ======== dmm_destroy ========
137  * Purpose:
138  * Release the communication memory manager resources.
139  */
140 int dmm_destroy(struct dmm_object *dmm_mgr)
141 {
142  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
143  int status = 0;
144 
145  if (dmm_mgr) {
146  status = dmm_delete_tables(dmm_obj);
147  if (!status)
148  kfree(dmm_obj);
149  } else
150  status = -EFAULT;
151 
152  return status;
153 }
154 
155 /*
156  * ======== dmm_delete_tables ========
157  * Purpose:
158  * Delete DMM Tables.
159  */
160 int dmm_delete_tables(struct dmm_object *dmm_mgr)
161 {
162  int status = 0;
163 
164  /* Delete all DMM tables */
165  if (dmm_mgr)
166  vfree(virtual_mapping_table);
167  else
168  status = -EFAULT;
169  return status;
170 }
171 
172 /*
173  * ======== dmm_get_handle ========
174  * Purpose:
175  * Return the dynamic memory manager object for this device.
176  * This is typically called from the client process.
177  */
178 int dmm_get_handle(void *hprocessor, struct dmm_object **dmm_manager)
179 {
180  int status = 0;
181  struct dev_object *hdev_obj;
182 
183  if (hprocessor != NULL)
184  status = proc_get_dev_object(hprocessor, &hdev_obj);
185  else
186  hdev_obj = dev_get_first(); /* default */
187 
188  if (!status)
189  status = dev_get_dmm_mgr(hdev_obj, dmm_manager);
190 
191  return status;
192 }
193 
194 /*
195  * ======== dmm_map_memory ========
196  * Purpose:
197  * Add a mapping block to the reserved chunk. DMM assumes that this block
198  * will be mapped in the DSP/IVA's address space. DMM returns an error if a
199  * mapping overlaps another one. This function stores the info that will be
200  * required later while unmapping the block.
201  */
203 {
204  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
205  struct map_page *chunk;
206  int status = 0;
207 
208  spin_lock(&dmm_obj->dmm_lock);
209  /* Find the Reserved memory chunk containing the DSP block to
210  * be mapped */
211  chunk = (struct map_page *)get_region(addr);
212  if (chunk != NULL) {
213  /* Mark the region 'mapped', leave the 'reserved' info as-is */
214  chunk->mapped = true;
215  chunk->mapped_size = (size / PG_SIZE4K);
216  } else
217  status = -ENOENT;
218  spin_unlock(&dmm_obj->dmm_lock);
219 
220  dev_dbg(bridge, "%s dmm_mgr %p, addr %x, size %x\n\tstatus %x, "
221  "chunk %p", __func__, dmm_mgr, addr, size, status, chunk);
222 
223  return status;
224 }
225 
226 /*
227  * ======== dmm_reserve_memory ========
228  * Purpose:
229  * Reserve a chunk of virtually contiguous DSP/IVA address space.
230  */
231 int dmm_reserve_memory(struct dmm_object *dmm_mgr, u32 size,
232  u32 *prsv_addr)
233 {
234  int status = 0;
235  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
236  struct map_page *node;
237  u32 rsv_addr = 0;
238  u32 rsv_size = 0;
239 
240  spin_lock(&dmm_obj->dmm_lock);
241 
242  /* Try to get a DSP chunk from the free list */
243  node = get_free_region(size);
244  if (node != NULL) {
245  /* DSP chunk of given size is available. */
246  rsv_addr = DMM_ADDR_VIRTUAL(node);
247  /* Calculate the number entries to use */
248  rsv_size = size / PG_SIZE4K;
249  if (rsv_size < node->region_size) {
250  /* Mark remainder of free region */
251  node[rsv_size].mapped = false;
252  node[rsv_size].reserved = false;
253  node[rsv_size].region_size =
254  node->region_size - rsv_size;
255  node[rsv_size].mapped_size = 0;
256  }
257  /* get_region will return first fit chunk. But we only use what
258  is requested. */
259  node->mapped = false;
260  node->reserved = true;
261  node->region_size = rsv_size;
262  node->mapped_size = 0;
263  /* Return the chunk's starting address */
264  *prsv_addr = rsv_addr;
265  } else
266  /*dSP chunk of given size is not available */
267  status = -ENOMEM;
268 
269  spin_unlock(&dmm_obj->dmm_lock);
270 
271  dev_dbg(bridge, "%s dmm_mgr %p, size %x, prsv_addr %p\n\tstatus %x, "
272  "rsv_addr %x, rsv_size %x\n", __func__, dmm_mgr, size,
273  prsv_addr, status, rsv_addr, rsv_size);
274 
275  return status;
276 }
277 
278 /*
279  * ======== dmm_un_map_memory ========
280  * Purpose:
281  * Remove the mapped block from the reserved chunk.
282  */
283 int dmm_un_map_memory(struct dmm_object *dmm_mgr, u32 addr, u32 *psize)
284 {
285  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
286  struct map_page *chunk;
287  int status = 0;
288 
289  spin_lock(&dmm_obj->dmm_lock);
290  chunk = get_mapped_region(addr);
291  if (chunk == NULL)
292  status = -ENOENT;
293 
294  if (!status) {
295  /* Unmap the region */
296  *psize = chunk->mapped_size * PG_SIZE4K;
297  chunk->mapped = false;
298  chunk->mapped_size = 0;
299  }
300  spin_unlock(&dmm_obj->dmm_lock);
301 
302  dev_dbg(bridge, "%s: dmm_mgr %p, addr %x, psize %p\n\tstatus %x, "
303  "chunk %p\n", __func__, dmm_mgr, addr, psize, status, chunk);
304 
305  return status;
306 }
307 
308 /*
309  * ======== dmm_un_reserve_memory ========
310  * Purpose:
311  * Free a chunk of reserved DSP/IVA address space.
312  */
313 int dmm_un_reserve_memory(struct dmm_object *dmm_mgr, u32 rsv_addr)
314 {
315  struct dmm_object *dmm_obj = (struct dmm_object *)dmm_mgr;
316  struct map_page *chunk;
317  u32 i;
318  int status = 0;
319  u32 chunk_size;
320 
321  spin_lock(&dmm_obj->dmm_lock);
322 
323  /* Find the chunk containing the reserved address */
324  chunk = get_mapped_region(rsv_addr);
325  if (chunk == NULL)
326  status = -ENOENT;
327 
328  if (!status) {
329  /* Free all the mapped pages for this reserved region */
330  i = 0;
331  while (i < chunk->region_size) {
332  if (chunk[i].mapped) {
333  /* Remove mapping from the page tables. */
334  chunk_size = chunk[i].mapped_size;
335  /* Clear the mapping flags */
336  chunk[i].mapped = false;
337  chunk[i].mapped_size = 0;
338  i += chunk_size;
339  } else
340  i++;
341  }
342  /* Clear the flags (mark the region 'free') */
343  chunk->reserved = false;
344  /* NOTE: We do NOT coalesce free regions here.
345  * Free regions are coalesced in get_region(), as it traverses
346  *the whole mapping table
347  */
348  }
349  spin_unlock(&dmm_obj->dmm_lock);
350 
351  dev_dbg(bridge, "%s: dmm_mgr %p, rsv_addr %x\n\tstatus %x chunk %p",
352  __func__, dmm_mgr, rsv_addr, status, chunk);
353 
354  return status;
355 }
356 
357 /*
358  * ======== get_region ========
359  * Purpose:
360  * Returns a region containing the specified memory region
361  */
362 static struct map_page *get_region(u32 addr)
363 {
364  struct map_page *curr_region = NULL;
365  u32 i = 0;
366 
367  if (virtual_mapping_table != NULL) {
368  /* find page mapped by this address */
369  i = DMM_ADDR_TO_INDEX(addr);
370  if (i < table_size)
371  curr_region = virtual_mapping_table + i;
372  }
373 
374  dev_dbg(bridge, "%s: curr_region %p, free_region %d, free_size %d\n",
375  __func__, curr_region, free_region, free_size);
376  return curr_region;
377 }
378 
379 /*
380  * ======== get_free_region ========
381  * Purpose:
382  * Returns the requested free region
383  */
384 static struct map_page *get_free_region(u32 len)
385 {
386  struct map_page *curr_region = NULL;
387  u32 i = 0;
388  u32 region_size = 0;
389  u32 next_i = 0;
390 
391  if (virtual_mapping_table == NULL)
392  return curr_region;
393  if (len > free_size) {
394  /* Find the largest free region
395  * (coalesce during the traversal) */
396  while (i < table_size) {
397  region_size = virtual_mapping_table[i].region_size;
398  next_i = i + region_size;
399  if (virtual_mapping_table[i].reserved == false) {
400  /* Coalesce, if possible */
401  if (next_i < table_size &&
402  virtual_mapping_table[next_i].reserved
403  == false) {
404  virtual_mapping_table[i].region_size +=
405  virtual_mapping_table
406  [next_i].region_size;
407  continue;
408  }
409  region_size *= PG_SIZE4K;
410  if (region_size > free_size) {
411  free_region = i;
412  free_size = region_size;
413  }
414  }
415  i = next_i;
416  }
417  }
418  if (len <= free_size) {
419  curr_region = virtual_mapping_table + free_region;
420  free_region += (len / PG_SIZE4K);
421  free_size -= len;
422  }
423  return curr_region;
424 }
425 
426 /*
427  * ======== get_mapped_region ========
428  * Purpose:
429  * Returns the requestedmapped region
430  */
431 static struct map_page *get_mapped_region(u32 addrs)
432 {
433  u32 i = 0;
434  struct map_page *curr_region = NULL;
435 
436  if (virtual_mapping_table == NULL)
437  return curr_region;
438 
439  i = DMM_ADDR_TO_INDEX(addrs);
440  if (i < table_size && (virtual_mapping_table[i].mapped ||
441  virtual_mapping_table[i].reserved))
442  curr_region = virtual_mapping_table + i;
443  return curr_region;
444 }
445 
446 #ifdef DSP_DMM_DEBUG
447 u32 dmm_mem_map_dump(struct dmm_object *dmm_mgr)
448 {
449  struct map_page *curr_node = NULL;
450  u32 i;
451  u32 freemem = 0;
452  u32 bigsize = 0;
453 
454  spin_lock(&dmm_mgr->dmm_lock);
455 
456  if (virtual_mapping_table != NULL) {
457  for (i = 0; i < table_size; i +=
458  virtual_mapping_table[i].region_size) {
459  curr_node = virtual_mapping_table + i;
460  if (curr_node->reserved) {
461  /*printk("RESERVED size = 0x%x, "
462  "Map size = 0x%x\n",
463  (curr_node->region_size * PG_SIZE4K),
464  (curr_node->mapped == false) ? 0 :
465  (curr_node->mapped_size * PG_SIZE4K));
466  */
467  } else {
468 /* printk("UNRESERVED size = 0x%x\n",
469  (curr_node->region_size * PG_SIZE4K));
470  */
471  freemem += (curr_node->region_size * PG_SIZE4K);
472  if (curr_node->region_size > bigsize)
473  bigsize = curr_node->region_size;
474  }
475  }
476  }
477  spin_unlock(&dmm_mgr->dmm_lock);
478  printk(KERN_INFO "Total DSP VA FREE memory = %d Mbytes\n",
479  freemem / (1024 * 1024));
480  printk(KERN_INFO "Total DSP VA USED memory= %d Mbytes \n",
481  (((table_size * PG_SIZE4K) - freemem)) / (1024 * 1024));
482  printk(KERN_INFO "DSP VA - Biggest FREE block = %d Mbytes \n\n",
483  (bigsize * PG_SIZE4K / (1024 * 1024)));
484 
485  return 0;
486 }
487 #endif