diff options
Diffstat (limited to 'drivers/s390/cio/itcw.c')
-rw-r--r-- | drivers/s390/cio/itcw.c | 62 |
1 files changed, 52 insertions, 10 deletions
diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c index a0ae29564774..358ee16d10a2 100644 --- a/drivers/s390/cio/itcw.c +++ b/drivers/s390/cio/itcw.c | |||
@@ -93,6 +93,7 @@ EXPORT_SYMBOL(itcw_get_tcw); | |||
93 | size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) | 93 | size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) |
94 | { | 94 | { |
95 | size_t len; | 95 | size_t len; |
96 | int cross_count; | ||
96 | 97 | ||
97 | /* Main data. */ | 98 | /* Main data. */ |
98 | len = sizeof(struct itcw); | 99 | len = sizeof(struct itcw); |
@@ -105,12 +106,27 @@ size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) | |||
105 | /* TSB */ sizeof(struct tsb) + | 106 | /* TSB */ sizeof(struct tsb) + |
106 | /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); | 107 | /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); |
107 | } | 108 | } |
109 | |||
108 | /* Maximum required alignment padding. */ | 110 | /* Maximum required alignment padding. */ |
109 | len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; | 111 | len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; |
110 | /* Maximum padding for structures that may not cross 4k boundary. */ | 112 | |
111 | if ((max_tidaws > 0) || (intrg_max_tidaws > 0)) | 113 | /* TIDAW lists may not cross a 4k boundary. To cross a |
112 | len += max(max_tidaws, intrg_max_tidaws) * | 114 | * boundary we need to add a TTIC TIDAW. We need to reserve |
113 | sizeof(struct tidaw) - 1; | 115 | * one additional TIDAW for a TTIC that we may need to add due |
116 | * to the placement of the data chunk in memory, and a further | ||
117 | * TIDAW for each page boundary that the TIDAW list may cross | ||
118 | * due to it's own size. | ||
119 | */ | ||
120 | if (max_tidaws) { | ||
121 | cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) | ||
122 | >> PAGE_SHIFT); | ||
123 | len += cross_count * sizeof(struct tidaw); | ||
124 | } | ||
125 | if (intrg_max_tidaws) { | ||
126 | cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) | ||
127 | >> PAGE_SHIFT); | ||
128 | len += cross_count * sizeof(struct tidaw); | ||
129 | } | ||
114 | return len; | 130 | return len; |
115 | } | 131 | } |
116 | EXPORT_SYMBOL(itcw_calc_size); | 132 | EXPORT_SYMBOL(itcw_calc_size); |
@@ -165,6 +181,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
165 | void *chunk; | 181 | void *chunk; |
166 | addr_t start; | 182 | addr_t start; |
167 | addr_t end; | 183 | addr_t end; |
184 | int cross_count; | ||
168 | 185 | ||
169 | /* Check for 2G limit. */ | 186 | /* Check for 2G limit. */ |
170 | start = (addr_t) buffer; | 187 | start = (addr_t) buffer; |
@@ -177,8 +194,17 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
177 | if (IS_ERR(chunk)) | 194 | if (IS_ERR(chunk)) |
178 | return chunk; | 195 | return chunk; |
179 | itcw = chunk; | 196 | itcw = chunk; |
180 | itcw->max_tidaws = max_tidaws; | 197 | /* allow for TTIC tidaws that may be needed to cross a page boundary */ |
181 | itcw->intrg_max_tidaws = intrg_max_tidaws; | 198 | cross_count = 0; |
199 | if (max_tidaws) | ||
200 | cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) | ||
201 | >> PAGE_SHIFT); | ||
202 | itcw->max_tidaws = max_tidaws + cross_count; | ||
203 | cross_count = 0; | ||
204 | if (intrg_max_tidaws) | ||
205 | cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) | ||
206 | >> PAGE_SHIFT); | ||
207 | itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count; | ||
182 | /* Main TCW. */ | 208 | /* Main TCW. */ |
183 | chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); | 209 | chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); |
184 | if (IS_ERR(chunk)) | 210 | if (IS_ERR(chunk)) |
@@ -198,7 +224,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
198 | /* Data TIDAL. */ | 224 | /* Data TIDAL. */ |
199 | if (max_tidaws > 0) { | 225 | if (max_tidaws > 0) { |
200 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * | 226 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * |
201 | max_tidaws, 16, 1); | 227 | itcw->max_tidaws, 16, 0); |
202 | if (IS_ERR(chunk)) | 228 | if (IS_ERR(chunk)) |
203 | return chunk; | 229 | return chunk; |
204 | tcw_set_data(itcw->tcw, chunk, 1); | 230 | tcw_set_data(itcw->tcw, chunk, 1); |
@@ -206,7 +232,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, | |||
206 | /* Interrogate data TIDAL. */ | 232 | /* Interrogate data TIDAL. */ |
207 | if (intrg && (intrg_max_tidaws > 0)) { | 233 | if (intrg && (intrg_max_tidaws > 0)) { |
208 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * | 234 | chunk = fit_chunk(&start, end, sizeof(struct tidaw) * |
209 | intrg_max_tidaws, 16, 1); | 235 | itcw->intrg_max_tidaws, 16, 0); |
210 | if (IS_ERR(chunk)) | 236 | if (IS_ERR(chunk)) |
211 | return chunk; | 237 | return chunk; |
212 | tcw_set_data(itcw->intrg_tcw, chunk, 1); | 238 | tcw_set_data(itcw->intrg_tcw, chunk, 1); |
@@ -283,13 +309,29 @@ EXPORT_SYMBOL(itcw_add_dcw); | |||
283 | * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the | 309 | * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the |
284 | * available space. | 310 | * available space. |
285 | * | 311 | * |
286 | * Note: the tidaw-list is assumed to be contiguous with no ttics. The | 312 | * Note: TTIC tidaws are automatically added when needed, so explicitly calling |
287 | * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize. | 313 | * this interface with the TTIC flag is not supported. The last-tidaw flag |
314 | * for the last tidaw in the list will be set by itcw_finalize. | ||
288 | */ | 315 | */ |
289 | struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) | 316 | struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) |
290 | { | 317 | { |
318 | struct tidaw *following; | ||
319 | |||
291 | if (itcw->num_tidaws >= itcw->max_tidaws) | 320 | if (itcw->num_tidaws >= itcw->max_tidaws) |
292 | return ERR_PTR(-ENOSPC); | 321 | return ERR_PTR(-ENOSPC); |
322 | /* | ||
323 | * Is the tidaw, which follows the one we are about to fill, on the next | ||
324 | * page? Then we have to insert a TTIC tidaw first, that points to the | ||
325 | * tidaw on the new page. | ||
326 | */ | ||
327 | following = ((struct tidaw *) tcw_get_data(itcw->tcw)) | ||
328 | + itcw->num_tidaws + 1; | ||
329 | if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) { | ||
330 | tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, | ||
331 | TIDAW_FLAGS_TTIC, following, 0); | ||
332 | if (itcw->num_tidaws >= itcw->max_tidaws) | ||
333 | return ERR_PTR(-ENOSPC); | ||
334 | } | ||
293 | return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); | 335 | return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); |
294 | } | 336 | } |
295 | EXPORT_SYMBOL(itcw_add_tidaw); | 337 | EXPORT_SYMBOL(itcw_add_tidaw); |