async_pq.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * Copyright(c) 2007 Yuri Tikhonov <yur@emcraft.com>
  3. * Copyright(c) 2009 Intel Corporation
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the Free
  7. * Software Foundation; either version 2 of the License, or (at your option)
  8. * any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with
  16. * this program; if not, write to the Free Software Foundation, Inc., 59
  17. * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. *
  19. * The full GNU General Public License is included in this distribution in the
  20. * file called COPYING.
  21. */
  22. #include <linux/kernel.h>
  23. #include <linux/interrupt.h>
  24. #include <linux/module.h>
  25. #include <linux/dma-mapping.h>
  26. #include <linux/raid/pq.h>
  27. #include <linux/async_tx.h>
  28. #include <linux/gfp.h>
  29. /**
  30. * pq_scribble_page - space to hold throwaway P or Q buffer for
  31. * synchronous gen_syndrome
  32. */
  33. static struct page *pq_scribble_page;
  34. /* the struct page *blocks[] parameter passed to async_gen_syndrome()
  35. * and async_syndrome_val() contains the 'P' destination address at
  36. * blocks[disks-2] and the 'Q' destination address at blocks[disks-1]
  37. *
  38. * note: these are macros as they are used as lvalues
  39. */
  40. #define P(b, d) (b[d-2])
  41. #define Q(b, d) (b[d-1])
  42. /**
  43. * do_async_gen_syndrome - asynchronously calculate P and/or Q
  44. */
  45. static __async_inline struct dma_async_tx_descriptor *
  46. do_async_gen_syndrome(struct dma_chan *chan, struct page **blocks,
  47. const unsigned char *scfs, unsigned int offset, int disks,
  48. size_t len, dma_addr_t *dma_src,
  49. struct async_submit_ctl *submit)
  50. {
  51. struct dma_async_tx_descriptor *tx = NULL;
  52. struct dma_device *dma = chan->device;
  53. enum dma_ctrl_flags dma_flags = 0;
  54. enum async_tx_flags flags_orig = submit->flags;
  55. dma_async_tx_callback cb_fn_orig = submit->cb_fn;
  56. dma_async_tx_callback cb_param_orig = submit->cb_param;
  57. int src_cnt = disks - 2;
  58. unsigned char coefs[src_cnt];
  59. unsigned short pq_src_cnt;
  60. dma_addr_t dma_dest[2];
  61. int src_off = 0;
  62. int idx;
  63. int i;
  64. /* DMAs use destinations as sources, so use BIDIRECTIONAL mapping */
  65. if (P(blocks, disks))
  66. dma_dest[0] = dma_map_page(dma->dev, P(blocks, disks), offset,
  67. len, DMA_BIDIRECTIONAL);
  68. else
  69. dma_flags |= DMA_PREP_PQ_DISABLE_P;
  70. if (Q(blocks, disks))
  71. dma_dest[1] = dma_map_page(dma->dev, Q(blocks, disks), offset,
  72. len, DMA_BIDIRECTIONAL);
  73. else
  74. dma_flags |= DMA_PREP_PQ_DISABLE_Q;
  75. /* convert source addresses being careful to collapse 'empty'
  76. * sources and update the coefficients accordingly
  77. */
  78. for (i = 0, idx = 0; i < src_cnt; i++) {
  79. if (blocks[i] == NULL)
  80. continue;
  81. dma_src[idx] = dma_map_page(dma->dev, blocks[i], offset, len,
  82. DMA_TO_DEVICE);
  83. coefs[idx] = scfs[i];
  84. idx++;
  85. }
  86. src_cnt = idx;
  87. while (src_cnt > 0) {
  88. submit->flags = flags_orig;
  89. pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags));
  90. /* if we are submitting additional pqs, leave the chain open,
  91. * clear the callback parameters, and leave the destination
  92. * buffers mapped
  93. */
  94. if (src_cnt > pq_src_cnt) {
  95. submit->flags &= ~ASYNC_TX_ACK;
  96. submit->flags |= ASYNC_TX_FENCE;
  97. dma_flags |= DMA_COMPL_SKIP_DEST_UNMAP;
  98. submit->cb_fn = NULL;
  99. submit->cb_param = NULL;
  100. } else {
  101. dma_flags &= ~DMA_COMPL_SKIP_DEST_UNMAP;
  102. submit->cb_fn = cb_fn_orig;
  103. submit->cb_param = cb_param_orig;
  104. if (cb_fn_orig)
  105. dma_flags |= DMA_PREP_INTERRUPT;
  106. }
  107. if (submit->flags & ASYNC_TX_FENCE)
  108. dma_flags |= DMA_PREP_FENCE;
  109. /* Since we have clobbered the src_list we are committed
  110. * to doing this asynchronously. Drivers force forward
  111. * progress in case they can not provide a descriptor
  112. */
  113. for (;;) {
  114. tx = dma->device_prep_dma_pq(chan, dma_dest,
  115. &dma_src[src_off],
  116. pq_src_cnt,
  117. &coefs[src_off], len,
  118. dma_flags);
  119. if (likely(tx))
  120. break;
  121. async_tx_quiesce(&submit->depend_tx);
  122. dma_async_issue_pending(chan);
  123. }
  124. async_tx_submit(chan, tx, submit);
  125. submit->depend_tx = tx;
  126. /* drop completed sources */
  127. src_cnt -= pq_src_cnt;
  128. src_off += pq_src_cnt;
  129. dma_flags |= DMA_PREP_CONTINUE;
  130. }
  131. return tx;
  132. }
  133. /**
  134. * do_sync_gen_syndrome - synchronously calculate a raid6 syndrome
  135. */
  136. static void
  137. do_sync_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
  138. size_t len, struct async_submit_ctl *submit)
  139. {
  140. void **srcs;
  141. int i;
  142. if (submit->scribble)
  143. srcs = submit->scribble;
  144. else
  145. srcs = (void **) blocks;
  146. for (i = 0; i < disks; i++) {
  147. if (blocks[i] == NULL) {
  148. BUG_ON(i > disks - 3); /* P or Q can't be zero */
  149. srcs[i] = (void*)raid6_empty_zero_page;
  150. } else
  151. srcs[i] = page_address(blocks[i]) + offset;
  152. }
  153. raid6_call.gen_syndrome(disks, len, srcs);
  154. async_tx_sync_epilog(submit);
  155. }
  156. /**
  157. * async_gen_syndrome - asynchronously calculate a raid6 syndrome
  158. * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1
  159. * @offset: common offset into each block (src and dest) to start transaction
  160. * @disks: number of blocks (including missing P or Q, see below)
  161. * @len: length of operation in bytes
  162. * @submit: submission/completion modifiers
  163. *
  164. * General note: This routine assumes a field of GF(2^8) with a
  165. * primitive polynomial of 0x11d and a generator of {02}.
  166. *
  167. * 'disks' note: callers can optionally omit either P or Q (but not
  168. * both) from the calculation by setting blocks[disks-2] or
  169. * blocks[disks-1] to NULL. When P or Q is omitted 'len' must be <=
  170. * PAGE_SIZE as a temporary buffer of this size is used in the
  171. * synchronous path. 'disks' always accounts for both destination
  172. * buffers. If any source buffers (blocks[i] where i < disks - 2) are
  173. * set to NULL those buffers will be replaced with the raid6_zero_page
  174. * in the synchronous path and omitted in the hardware-asynchronous
  175. * path.
  176. *
  177. * 'blocks' note: if submit->scribble is NULL then the contents of
  178. * 'blocks' may be overwritten to perform address conversions
  179. * (dma_map_page() or page_address()).
  180. */
  181. struct dma_async_tx_descriptor *
  182. async_gen_syndrome(struct page **blocks, unsigned int offset, int disks,
  183. size_t len, struct async_submit_ctl *submit)
  184. {
  185. int src_cnt = disks - 2;
  186. struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
  187. &P(blocks, disks), 2,
  188. blocks, src_cnt, len);
  189. struct dma_device *device = chan ? chan->device : NULL;
  190. dma_addr_t *dma_src = NULL;
  191. BUG_ON(disks > 255 || !(P(blocks, disks) || Q(blocks, disks)));
  192. if (submit->scribble)
  193. dma_src = submit->scribble;
  194. else if (sizeof(dma_addr_t) <= sizeof(struct page *))
  195. dma_src = (dma_addr_t *) blocks;
  196. if (dma_src && device &&
  197. (src_cnt <= dma_maxpq(device, 0) ||
  198. dma_maxpq(device, DMA_PREP_CONTINUE) > 0) &&
  199. is_dma_pq_aligned(device, offset, 0, len)) {
  200. /* run the p+q asynchronously */
  201. pr_debug("%s: (async) disks: %d len: %zu\n",
  202. __func__, disks, len);
  203. return do_async_gen_syndrome(chan, blocks, raid6_gfexp, offset,
  204. disks, len, dma_src, submit);
  205. }
  206. /* run the pq synchronously */
  207. pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len);
  208. /* wait for any prerequisite operations */
  209. async_tx_quiesce(&submit->depend_tx);
  210. if (!P(blocks, disks)) {
  211. P(blocks, disks) = pq_scribble_page;
  212. BUG_ON(len + offset > PAGE_SIZE);
  213. }
  214. if (!Q(blocks, disks)) {
  215. Q(blocks, disks) = pq_scribble_page;
  216. BUG_ON(len + offset > PAGE_SIZE);
  217. }
  218. do_sync_gen_syndrome(blocks, offset, disks, len, submit);
  219. return NULL;
  220. }
  221. EXPORT_SYMBOL_GPL(async_gen_syndrome);
  222. static inline struct dma_chan *
  223. pq_val_chan(struct async_submit_ctl *submit, struct page **blocks, int disks, size_t len)
  224. {
  225. #ifdef CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA
  226. return NULL;
  227. #endif
  228. return async_tx_find_channel(submit, DMA_PQ_VAL, NULL, 0, blocks,
  229. disks, len);
  230. }
  231. /**
  232. * async_syndrome_val - asynchronously validate a raid6 syndrome
  233. * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1
  234. * @offset: common offset into each block (src and dest) to start transaction
  235. * @disks: number of blocks (including missing P or Q, see below)
  236. * @len: length of operation in bytes
  237. * @pqres: on val failure SUM_CHECK_P_RESULT and/or SUM_CHECK_Q_RESULT are set
  238. * @spare: temporary result buffer for the synchronous case
  239. * @submit: submission / completion modifiers
  240. *
  241. * The same notes from async_gen_syndrome apply to the 'blocks',
  242. * and 'disks' parameters of this routine. The synchronous path
  243. * requires a temporary result buffer and submit->scribble to be
  244. * specified.
  245. */
  246. struct dma_async_tx_descriptor *
  247. async_syndrome_val(struct page **blocks, unsigned int offset, int disks,
  248. size_t len, enum sum_check_flags *pqres, struct page *spare,
  249. struct async_submit_ctl *submit)
  250. {
  251. struct dma_chan *chan = pq_val_chan(submit, blocks, disks, len);
  252. struct dma_device *device = chan ? chan->device : NULL;
  253. struct dma_async_tx_descriptor *tx;
  254. unsigned char coefs[disks-2];
  255. enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0;
  256. dma_addr_t *dma_src = NULL;
  257. int src_cnt = 0;
  258. BUG_ON(disks < 4);
  259. if (submit->scribble)
  260. dma_src = submit->scribble;
  261. else if (sizeof(dma_addr_t) <= sizeof(struct page *))
  262. dma_src = (dma_addr_t *) blocks;
  263. if (dma_src && device && disks <= dma_maxpq(device, 0) &&
  264. is_dma_pq_aligned(device, offset, 0, len)) {
  265. struct device *dev = device->dev;
  266. dma_addr_t *pq = &dma_src[disks-2];
  267. int i;
  268. pr_debug("%s: (async) disks: %d len: %zu\n",
  269. __func__, disks, len);
  270. if (!P(blocks, disks))
  271. dma_flags |= DMA_PREP_PQ_DISABLE_P;
  272. else
  273. pq[0] = dma_map_page(dev, P(blocks, disks),
  274. offset, len,
  275. DMA_TO_DEVICE);
  276. if (!Q(blocks, disks))
  277. dma_flags |= DMA_PREP_PQ_DISABLE_Q;
  278. else
  279. pq[1] = dma_map_page(dev, Q(blocks, disks),
  280. offset, len,
  281. DMA_TO_DEVICE);
  282. if (submit->flags & ASYNC_TX_FENCE)
  283. dma_flags |= DMA_PREP_FENCE;
  284. for (i = 0; i < disks-2; i++)
  285. if (likely(blocks[i])) {
  286. dma_src[src_cnt] = dma_map_page(dev, blocks[i],
  287. offset, len,
  288. DMA_TO_DEVICE);
  289. coefs[src_cnt] = raid6_gfexp[i];
  290. src_cnt++;
  291. }
  292. for (;;) {
  293. tx = device->device_prep_dma_pq_val(chan, pq, dma_src,
  294. src_cnt,
  295. coefs,
  296. len, pqres,
  297. dma_flags);
  298. if (likely(tx))
  299. break;
  300. async_tx_quiesce(&submit->depend_tx);
  301. dma_async_issue_pending(chan);
  302. }
  303. async_tx_submit(chan, tx, submit);
  304. return tx;
  305. } else {
  306. struct page *p_src = P(blocks, disks);
  307. struct page *q_src = Q(blocks, disks);
  308. enum async_tx_flags flags_orig = submit->flags;
  309. dma_async_tx_callback cb_fn_orig = submit->cb_fn;
  310. void *scribble = submit->scribble;
  311. void *cb_param_orig = submit->cb_param;
  312. void *p, *q, *s;
  313. pr_debug("%s: (sync) disks: %d len: %zu\n",
  314. __func__, disks, len);
  315. /* caller must provide a temporary result buffer and
  316. * allow the input parameters to be preserved
  317. */
  318. BUG_ON(!spare || !scribble);
  319. /* wait for any prerequisite operations */
  320. async_tx_quiesce(&submit->depend_tx);
  321. /* recompute p and/or q into the temporary buffer and then
  322. * check to see the result matches the current value
  323. */
  324. tx = NULL;
  325. *pqres = 0;
  326. if (p_src) {
  327. init_async_submit(submit, ASYNC_TX_XOR_ZERO_DST, NULL,
  328. NULL, NULL, scribble);
  329. tx = async_xor(spare, blocks, offset, disks-2, len, submit);
  330. async_tx_quiesce(&tx);
  331. p = page_address(p_src) + offset;
  332. s = page_address(spare) + offset;
  333. *pqres |= !!memcmp(p, s, len) << SUM_CHECK_P;
  334. }
  335. if (q_src) {
  336. P(blocks, disks) = NULL;
  337. Q(blocks, disks) = spare;
  338. init_async_submit(submit, 0, NULL, NULL, NULL, scribble);
  339. tx = async_gen_syndrome(blocks, offset, disks, len, submit);
  340. async_tx_quiesce(&tx);
  341. q = page_address(q_src) + offset;
  342. s = page_address(spare) + offset;
  343. *pqres |= !!memcmp(q, s, len) << SUM_CHECK_Q;
  344. }
  345. /* restore P, Q and submit */
  346. P(blocks, disks) = p_src;
  347. Q(blocks, disks) = q_src;
  348. submit->cb_fn = cb_fn_orig;
  349. submit->cb_param = cb_param_orig;
  350. submit->flags = flags_orig;
  351. async_tx_sync_epilog(submit);
  352. return NULL;
  353. }
  354. }
  355. EXPORT_SYMBOL_GPL(async_syndrome_val);
  356. static int __init async_pq_init(void)
  357. {
  358. pq_scribble_page = alloc_page(GFP_KERNEL);
  359. if (pq_scribble_page)
  360. return 0;
  361. pr_err("%s: failed to allocate required spare page\n", __func__);
  362. return -ENOMEM;
  363. }
  364. static void __exit async_pq_exit(void)
  365. {
  366. put_page(pq_scribble_page);
  367. }
  368. module_init(async_pq_init);
  369. module_exit(async_pq_exit);
  370. MODULE_DESCRIPTION("asynchronous raid6 syndrome generation/validation");
  371. MODULE_LICENSE("GPL");