Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
itcw.c
Go to the documentation of this file.
1 /*
2  * Functions for incremental construction of fcx enabled I/O control blocks.
3  *
4  * Copyright IBM Corp. 2008
5  * Author(s): Peter Oberparleiter <[email protected]>
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/types.h>
10 #include <linux/string.h>
11 #include <linux/errno.h>
12 #include <linux/err.h>
13 #include <linux/module.h>
14 #include <asm/fcx.h>
15 #include <asm/itcw.h>
16 
57 struct itcw {
58  struct tcw *tcw;
59  struct tcw *intrg_tcw;
64 };
65 
72 struct tcw *itcw_get_tcw(struct itcw *itcw)
73 {
74  return itcw->tcw;
75 }
77 
93 size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
94 {
95  size_t len;
96  int cross_count;
97 
98  /* Main data. */
99  len = sizeof(struct itcw);
100  len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
101  /* TSB */ sizeof(struct tsb) +
102  /* TIDAL */ max_tidaws * sizeof(struct tidaw);
103  /* Interrogate data. */
104  if (intrg) {
105  len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
106  /* TSB */ sizeof(struct tsb) +
107  /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw);
108  }
109 
110  /* Maximum required alignment padding. */
111  len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7;
112 
113  /* TIDAW lists may not cross a 4k boundary. To cross a
114  * boundary we need to add a TTIC TIDAW. We need to reserve
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  }
130  return len;
131 }
133 
134 #define CROSS4K(x, l) (((x) & ~4095) != ((x + l) & ~4095))
135 
136 static inline void *fit_chunk(addr_t *start, addr_t end, size_t len,
137  int align, int check_4k)
138 {
139  addr_t addr;
140 
141  addr = ALIGN(*start, align);
142  if (check_4k && CROSS4K(addr, len)) {
143  addr = ALIGN(addr, 4096);
144  addr = ALIGN(addr, align);
145  }
146  if (addr + len > end)
147  return ERR_PTR(-ENOSPC);
148  *start = addr + len;
149  return (void *) addr;
150 }
151 
177 struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
178  int max_tidaws, int intrg_max_tidaws)
179 {
180  struct itcw *itcw;
181  void *chunk;
182  addr_t start;
183  addr_t end;
184  int cross_count;
185 
186  /* Check for 2G limit. */
187  start = (addr_t) buffer;
188  end = start + size;
189  if (end > (1 << 31))
190  return ERR_PTR(-EINVAL);
191  memset(buffer, 0, size);
192  /* ITCW. */
193  chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0);
194  if (IS_ERR(chunk))
195  return chunk;
196  itcw = chunk;
197  /* allow for TTIC tidaws that may be needed to cross a page boundary */
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;
208  /* Main TCW. */
209  chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
210  if (IS_ERR(chunk))
211  return chunk;
212  itcw->tcw = chunk;
213  tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0,
214  (op == ITCW_OP_WRITE) ? 1 : 0);
215  /* Interrogate TCW. */
216  if (intrg) {
217  chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
218  if (IS_ERR(chunk))
219  return chunk;
220  itcw->intrg_tcw = chunk;
221  tcw_init(itcw->intrg_tcw, 1, 0);
222  tcw_set_intrg(itcw->tcw, itcw->intrg_tcw);
223  }
224  /* Data TIDAL. */
225  if (max_tidaws > 0) {
226  chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
227  itcw->max_tidaws, 16, 0);
228  if (IS_ERR(chunk))
229  return chunk;
230  tcw_set_data(itcw->tcw, chunk, 1);
231  }
232  /* Interrogate data TIDAL. */
233  if (intrg && (intrg_max_tidaws > 0)) {
234  chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
235  itcw->intrg_max_tidaws, 16, 0);
236  if (IS_ERR(chunk))
237  return chunk;
238  tcw_set_data(itcw->intrg_tcw, chunk, 1);
239  }
240  /* TSB. */
241  chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
242  if (IS_ERR(chunk))
243  return chunk;
244  tsb_init(chunk);
245  tcw_set_tsb(itcw->tcw, chunk);
246  /* Interrogate TSB. */
247  if (intrg) {
248  chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
249  if (IS_ERR(chunk))
250  return chunk;
251  tsb_init(chunk);
252  tcw_set_tsb(itcw->intrg_tcw, chunk);
253  }
254  /* TCCB. */
255  chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
256  if (IS_ERR(chunk))
257  return chunk;
259  tcw_set_tccb(itcw->tcw, chunk);
260  /* Interrogate TCCB. */
261  if (intrg) {
262  chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
263  if (IS_ERR(chunk))
264  return chunk;
266  tcw_set_tccb(itcw->intrg_tcw, chunk);
268  sizeof(struct dcw_intrg_data), 0);
269  tcw_finalize(itcw->intrg_tcw, 0);
270  }
271  return itcw;
272 }
274 
292 struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
293  u8 cd_count, u32 count)
294 {
295  return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd,
296  flags, cd, cd_count, count);
297 }
299 
316 struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count)
317 {
318  struct tidaw *following;
319 
320  if (itcw->num_tidaws >= itcw->max_tidaws)
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  }
335  return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count);
336 }
338 
350 void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal)
351 {
352  tcw_set_data(itcw->tcw, addr, use_tidal);
353 }
355 
365 void itcw_finalize(struct itcw *itcw)
366 {
367  tcw_finalize(itcw->tcw, itcw->num_tidaws);
368 }