@@ -90,6 +90,8 @@ struct its_node {
u32 ite_size;
u32 device_ids;
int numa_node;
+ struct page *ite_page;
+ u32 ite_psz;
};
#define ITS_ITT_ALIGN SZ_256
@@ -266,7 +268,6 @@ static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd,
u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites);
itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt);
- itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN);
its_encode_cmd(cmd, GITS_CMD_MAPD);
its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id);
@@ -1319,6 +1320,42 @@ static bool its_alloc_device_table(struct its_node *its, u32 dev_id)
return true;
}
+static void *its_alloc_memory_ites(struct its_node *its, int nr_ites)
+{
+ unsigned long flags;
+ struct page *page;
+ void *ite;
+ u32 size;
+
+ size = ALIGN(nr_ites * its->ite_size, ITS_ITT_ALIGN);
+ raw_spin_lock_irqsave(&its->lock, flags);
+
+ /* Try to reuse the current page if enough space is available */
+ if (size > its->ite_psz) {
+ /* Allocate a new compound page with minimum order 1 */
+ page = alloc_pages(GFP_KERNEL | __GFP_COMP | __GFP_ZERO,
+ max(get_order(size), 1));
+ if (!page) {
+ raw_spin_unlock_irqrestore(&its->lock, flags);
+ return NULL;
+ }
+
+ /* Free current page, decrement page count */
+ if (its->ite_page)
+ put_page(its->ite_page);
+ its->ite_psz = PAGE_ORDER_TO_SIZE(compound_order(page));
+ its->ite_page = page;
+ }
+
+ get_page(its->ite_page); /* increment page count */
+ its->ite_psz -= size; /* update free space */
+ ite = page_address(its->ite_page) + its->ite_psz;
+ raw_spin_unlock_irqrestore(&its->lock, flags);
+ gic_flush_dcache_to_poc(ite, size);
+
+ return ite;
+}
+
static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
int nvecs)
{
@@ -1330,7 +1367,6 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
int lpi_base;
int nr_lpis;
int nr_ites;
- int sz;
if (!its_alloc_device_table(its, dev_id))
return NULL;
@@ -1342,22 +1378,22 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
* express an ITT with a single entry.
*/
nr_ites = max(2UL, roundup_pow_of_two(nvecs));
- sz = nr_ites * its->ite_size;
- sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
- itt = kzalloc(sz, GFP_KERNEL);
+ itt = its_alloc_memory_ites(its, nr_ites);
+ if (!itt)
+ return NULL;
+
lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);
if (lpi_map)
col_map = kzalloc(sizeof(*col_map) * nr_lpis, GFP_KERNEL);
- if (!dev || !itt || !lpi_map || !col_map) {
+ if (!dev || !lpi_map || !col_map) {
kfree(dev);
- kfree(itt);
+ put_page(virt_to_page(itt));
kfree(lpi_map);
kfree(col_map);
return NULL;
}
- gic_flush_dcache_to_poc(itt, sz);
dev->its = its;
dev->itt = itt;
@@ -1386,7 +1422,7 @@ static void its_free_device(struct its_device *its_dev)
raw_spin_lock_irqsave(&its_dev->its->lock, flags);
list_del(&its_dev->entry);
raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);
- kfree(its_dev->itt);
+ put_page(virt_to_page(its_dev->itt));
kfree(its_dev);
}