|
@@ -93,6 +93,7 @@ EXPORT_SYMBOL(itcw_get_tcw);
|
|
|
size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
|
|
|
{
|
|
|
size_t len;
|
|
|
+ int cross_count;
|
|
|
|
|
|
/* Main data. */
|
|
|
len = sizeof(struct itcw);
|
|
@@ -105,12 +106,27 @@ size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
|
|
|
/* TSB */ sizeof(struct tsb) +
|
|
|
/* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw);
|
|
|
}
|
|
|
+
|
|
|
/* Maximum required alignment padding. */
|
|
|
len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7;
|
|
|
- /* Maximum padding for structures that may not cross 4k boundary. */
|
|
|
- if ((max_tidaws > 0) || (intrg_max_tidaws > 0))
|
|
|
- len += max(max_tidaws, intrg_max_tidaws) *
|
|
|
- sizeof(struct tidaw) - 1;
|
|
|
+
|
|
|
+ /* TIDAW lists may not cross a 4k boundary. To cross a
|
|
|
+ * boundary we need to add a TTIC TIDAW. We need to reserve
|
|
|
+ * one additional TIDAW for a TTIC that we may need to add due
|
|
|
+ * to the placement of the data chunk in memory, and a further
|
|
|
+ * TIDAW for each page boundary that the TIDAW list may cross
|
|
|
+ * due to it's own size.
|
|
|
+ */
|
|
|
+ if (max_tidaws) {
|
|
|
+ cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1)
|
|
|
+ >> PAGE_SHIFT);
|
|
|
+ len += cross_count * sizeof(struct tidaw);
|
|
|
+ }
|
|
|
+ if (intrg_max_tidaws) {
|
|
|
+ cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1)
|
|
|
+ >> PAGE_SHIFT);
|
|
|
+ len += cross_count * sizeof(struct tidaw);
|
|
|
+ }
|
|
|
return len;
|
|
|
}
|
|
|
EXPORT_SYMBOL(itcw_calc_size);
|
|
@@ -165,6 +181,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
|
|
|
void *chunk;
|
|
|
addr_t start;
|
|
|
addr_t end;
|
|
|
+ int cross_count;
|
|
|
|
|
|
/* Check for 2G limit. */
|
|
|
start = (addr_t) buffer;
|
|
@@ -177,8 +194,17 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
|
|
|
if (IS_ERR(chunk))
|
|
|
return chunk;
|
|
|
itcw = chunk;
|
|
|
- itcw->max_tidaws = max_tidaws;
|
|
|
- itcw->intrg_max_tidaws = intrg_max_tidaws;
|
|
|
+ /* allow for TTIC tidaws that may be needed to cross a page boundary */
|
|
|
+ cross_count = 0;
|
|
|
+ if (max_tidaws)
|
|
|
+ cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1)
|
|
|
+ >> PAGE_SHIFT);
|
|
|
+ itcw->max_tidaws = max_tidaws + cross_count;
|
|
|
+ cross_count = 0;
|
|
|
+ if (intrg_max_tidaws)
|
|
|
+ cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1)
|
|
|
+ >> PAGE_SHIFT);
|
|
|
+ itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count;
|
|
|
/* Main TCW. */
|
|
|
chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
|
|
|
if (IS_ERR(chunk))
|
|
@@ -198,7 +224,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
|
|
|
/* Data TIDAL. */
|
|
|
if (max_tidaws > 0) {
|
|
|
chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
|
|
|
- max_tidaws, 16, 1);
|
|
|
+ itcw->max_tidaws, 16, 0);
|
|
|
if (IS_ERR(chunk))
|
|
|
return chunk;
|
|
|
tcw_set_data(itcw->tcw, chunk, 1);
|
|
@@ -206,7 +232,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
|
|
|
/* Interrogate data TIDAL. */
|
|
|
if (intrg && (intrg_max_tidaws > 0)) {
|
|
|
chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
|
|
|
- intrg_max_tidaws, 16, 1);
|
|
|
+ itcw->intrg_max_tidaws, 16, 0);
|
|
|
if (IS_ERR(chunk))
|
|
|
return chunk;
|
|
|
tcw_set_data(itcw->intrg_tcw, chunk, 1);
|
|
@@ -283,13 +309,29 @@ EXPORT_SYMBOL(itcw_add_dcw);
|
|
|
* the new tidaw on success or -%ENOSPC if the new tidaw would exceed the
|
|
|
* available space.
|
|
|
*
|
|
|
- * Note: the tidaw-list is assumed to be contiguous with no ttics. The
|
|
|
- * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize.
|
|
|
+ * Note: TTIC tidaws are automatically added when needed, so explicitly calling
|
|
|
+ * this interface with the TTIC flag is not supported. The last-tidaw flag
|
|
|
+ * for the last tidaw in the list will be set by itcw_finalize.
|
|
|
*/
|
|
|
struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count)
|
|
|
{
|
|
|
+ struct tidaw *following;
|
|
|
+
|
|
|
if (itcw->num_tidaws >= itcw->max_tidaws)
|
|
|
return ERR_PTR(-ENOSPC);
|
|
|
+ /*
|
|
|
+ * Is the tidaw, which follows the one we are about to fill, on the next
|
|
|
+ * page? Then we have to insert a TTIC tidaw first, that points to the
|
|
|
+ * tidaw on the new page.
|
|
|
+ */
|
|
|
+ following = ((struct tidaw *) tcw_get_data(itcw->tcw))
|
|
|
+ + itcw->num_tidaws + 1;
|
|
|
+ if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) {
|
|
|
+ tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++,
|
|
|
+ TIDAW_FLAGS_TTIC, following, 0);
|
|
|
+ if (itcw->num_tidaws >= itcw->max_tidaws)
|
|
|
+ return ERR_PTR(-ENOSPC);
|
|
|
+ }
|
|
|
return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count);
|
|
|
}
|
|
|
EXPORT_SYMBOL(itcw_add_tidaw);
|