1/*	$NetBSD: swdmover.c,v 1.14 2017/01/07 21:11:14 christos Exp $	*/
2
3/*
4 * Copyright (c) 2002, 2003 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed for the NetBSD Project by
20 *	Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 *    or promote products derived from this software without specific prior
23 *    written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38/*
39 * swdmover.c: Software back-end providing the dmover functions
40 * mentioned in dmover(9).
41 *
42 * This module provides a fallback for cases where no hardware
43 * data movers are present in a system, and also serves an an
44 * example of how to write a dmover back-end.
45 *
46 * Note that even through the software dmover doesn't require
47 * interrupts to be blocked, we block them anyway to demonstrate
48 * the locking protocol.
49 */
50
51#include <sys/cdefs.h>
52__KERNEL_RCSID(0, "$NetBSD: swdmover.c,v 1.14 2017/01/07 21:11:14 christos Exp $");
53
54#include <sys/param.h>
55#include <sys/kthread.h>
56#include <sys/systm.h>
57#include <sys/uio.h>
58
59#include <dev/dmover/dmovervar.h>
60
61#include "ioconf.h"
62
63struct swdmover_function {
64	void	(*sdf_process)(struct dmover_request *);
65};
66
67static struct dmover_backend swdmover_backend;
68static struct lwp *swdmover_lwp;
69static int swdmover_cv;
70
71/*
72 * swdmover_process:
73 *
74 *	Dmover back-end entry point.
75 */
76static void
77swdmover_process(struct dmover_backend *dmb)
78{
79	int s;
80
81	/*
82	 * Just wake up the processing thread.  This will allow
83	 * requests to linger on the middle-end's queue so that
84	 * they can be cancelled, if need-be.
85	 */
86	s = splbio();
87	/* XXXLOCK */
88	if (TAILQ_EMPTY(&dmb->dmb_pendreqs) == 0)
89		wakeup(&swdmover_cv);
90	/* XXXUNLOCK */
91	splx(s);
92}
93
94/*
95 * swdmover_thread:
96 *
97 *	Request processing thread.
98 */
99static void
100swdmover_thread(void *arg)
101{
102	struct dmover_backend *dmb = arg;
103	struct dmover_request *dreq;
104	struct swdmover_function *sdf;
105	int s;
106
107	s = splbio();
108	/* XXXLOCK */
109
110	for (;;) {
111		dreq = TAILQ_FIRST(&dmb->dmb_pendreqs);
112		if (dreq == NULL) {
113			/* XXXUNLOCK */
114			(void) tsleep(&swdmover_cv, PRIBIO, "swdmvr", 0);
115			continue;
116		}
117
118		dmover_backend_remque(dmb, dreq);
119		dreq->dreq_flags |= DMOVER_REQ_RUNNING;
120
121		/* XXXUNLOCK */
122		splx(s);
123
124		sdf = dreq->dreq_assignment->das_algdesc->dad_data;
125		(*sdf->sdf_process)(dreq);
126
127		s = splbio();
128		/* XXXLOCK */
129	}
130}
131
132/*
133 * swdmover_func_zero_process:
134 *
135 *	Processing routine for the "zero" function.
136 */
137static void
138swdmover_func_zero_process(struct dmover_request *dreq)
139{
140
141	switch (dreq->dreq_outbuf_type) {
142	case DMOVER_BUF_LINEAR:
143		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr, 0,
144		    dreq->dreq_outbuf.dmbuf_linear.l_len);
145		break;
146
147	case DMOVER_BUF_UIO:
148	    {
149		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
150		char cp[1024];
151		size_t count, buflen;
152		int error;
153
154		if (uio->uio_rw != UIO_READ) {
155			/* XXXLOCK */
156			dreq->dreq_error = EINVAL;
157			dreq->dreq_flags |= DMOVER_REQ_ERROR;
158			/* XXXUNLOCK */
159			break;
160		}
161
162		buflen = uio->uio_resid;
163		if (buflen > sizeof(cp))
164			buflen = sizeof(cp);
165		memset(cp, 0, buflen);
166
167		while ((count = uio->uio_resid) != 0) {
168			if (count > buflen)
169				count = buflen;
170			error = uiomove(cp, count, uio);
171			if (error) {
172				/* XXXLOCK */
173				dreq->dreq_error = error;
174				dreq->dreq_flags |= DMOVER_REQ_ERROR;
175				/* XXXUNLOCK */
176				break;
177			}
178		}
179		break;
180	    }
181
182	default:
183		/* XXXLOCK */
184		dreq->dreq_error = EINVAL;
185		dreq->dreq_flags |= DMOVER_REQ_ERROR;
186		/* XXXUNLOCK */
187	}
188
189	dmover_done(dreq);
190}
191
192/*
193 * swdmover_func_fill8_process:
194 *
195 *	Processing routine for the "fill8" function.
196 */
197static void
198swdmover_func_fill8_process(struct dmover_request *dreq)
199{
200
201	switch (dreq->dreq_outbuf_type) {
202	case DMOVER_BUF_LINEAR:
203		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr,
204		    dreq->dreq_immediate[0],
205		    dreq->dreq_outbuf.dmbuf_linear.l_len);
206		break;
207
208	case DMOVER_BUF_UIO:
209	    {
210		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
211		char cp[1024];
212		size_t count, buflen;
213		int error;
214
215		if (uio->uio_rw != UIO_READ) {
216			/* XXXLOCK */
217			dreq->dreq_error = EINVAL;
218			dreq->dreq_flags |= DMOVER_REQ_ERROR;
219			/* XXXUNLOCK */
220			break;
221		}
222
223		buflen = uio->uio_resid;
224		if (buflen > sizeof(cp))
225			buflen = sizeof(cp);
226		memset(cp, dreq->dreq_immediate[0], buflen);
227
228		while ((count = uio->uio_resid) != 0) {
229			if (count > buflen)
230				count = buflen;
231			error = uiomove(cp, count, uio);
232			if (error) {
233				/* XXXLOCK */
234				dreq->dreq_error = error;
235				dreq->dreq_flags |= DMOVER_REQ_ERROR;
236				/* XXXUNLOCK */
237				break;
238			}
239		}
240		break;
241	    }
242
243	default:
244		/* XXXLOCK */
245		dreq->dreq_error = EINVAL;
246		dreq->dreq_flags |= DMOVER_REQ_ERROR;
247		/* XXXUNLOCK */
248	}
249
250	dmover_done(dreq);
251}
252
253static void
254xor2(uint8_t *dst, uint8_t *src1, uint8_t *src2, int cnt)
255{
256
257	while (cnt--)
258		*dst++ = *src1++ ^ *src2++;
259}
260
261/*
262 * swdmover_func_xor_process:
263 *
264 *	Processing routine for the "xor" function.
265 */
266static void
267swdmover_func_xor_process(struct dmover_request *dreq)
268{
269#define INBUF_L(x)	dreq->dreq_inbuf[(x)].dmbuf_linear
270#define OUTBUF_L	dreq->dreq_outbuf.dmbuf_linear
271
272	uint32_t *dst32, *src32;
273	uint8_t *dst8, *src8;
274	int	i, ninputs = dreq->dreq_assignment->das_algdesc->dad_ninputs;
275	int	aligned, len, nwords;
276
277	/* XXX Currently, both buffers must be of same type. */
278	if (dreq->dreq_inbuf_type != dreq->dreq_outbuf_type) {
279		/* XXXLOCK */
280		dreq->dreq_error = EINVAL;
281		dreq->dreq_flags |= DMOVER_REQ_ERROR;
282		/* XXXUNLOCK */
283		goto done;
284	}
285
286	switch (dreq->dreq_outbuf_type) {
287	case DMOVER_BUF_LINEAR:
288		aligned = 1;
289		if ((ulong) OUTBUF_L.l_addr & 0x3)
290			aligned = 0;
291		len = OUTBUF_L.l_len;
292		for (i = 0 ; i < ninputs ; i++) {
293			if (len != INBUF_L(i).l_len) {
294				/* XXXLOCK */
295				dreq->dreq_error = EINVAL;
296				dreq->dreq_flags |= DMOVER_REQ_ERROR;
297				/* XXXUNLOCK */
298				break;
299			}
300			if ((ulong) INBUF_L(i).l_addr & 0x3)
301				aligned = 0;
302		}
303		if (aligned) {
304			dst32 = (uint32_t *) OUTBUF_L.l_addr;
305			nwords = len / 4;
306			while (nwords--) {
307				*dst32 = 0;
308				for (i = 0 ; i < ninputs ; i++) {
309					src32 = (uint32_t *) INBUF_L(i).l_addr;
310					*dst32 ^= *src32;
311				}
312				dst32++;
313				len -= 4;
314			}
315		}
316		if (len) {
317			dst8 = (uint8_t *) OUTBUF_L.l_addr;
318			while (len--) {
319				*dst8 = 0;
320				for (i = 0 ; i < ninputs ; i++) {
321					src8 = (uint8_t *) INBUF_L(i).l_addr;
322					*dst8 ^= *src8;
323				}
324				dst8++;
325			}
326		}
327
328		break;
329
330	case DMOVER_BUF_UIO:
331	    {
332		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
333		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
334		struct uio *uio;
335		char cp[1024], dst[1024];
336		size_t count, buflen;
337		int error;
338
339		if (uio_in->uio_rw != UIO_WRITE ||
340		    uio_out->uio_rw != UIO_READ ||
341		    uio_in->uio_resid != uio_out->uio_resid) {
342			/* XXXLOCK */
343			dreq->dreq_error = EINVAL;
344			dreq->dreq_flags |= DMOVER_REQ_ERROR;
345			/* XXXUNLOCK */
346			break;
347		}
348
349		buflen = uio_in->uio_resid;
350		if (buflen > sizeof(cp))
351			buflen = sizeof(cp);
352
353		/*
354		 * For each block, copy first input buffer into the destination
355		 * buffer and then read the rest, one by one, into a temporary
356		 * buffer and xor into the destination buffer.  After all of
357		 * the inputs have been xor'd in, move the destination buffer
358		 * out and loop.
359		 */
360		while ((count = uio_in->uio_resid) != 0) {
361			if (count > buflen)
362				count = buflen;
363			error = uiomove(dst, count, uio_in);
364			if (error) {
365				/* XXXLOCK */
366				dreq->dreq_error = error;
367				dreq->dreq_flags |= DMOVER_REQ_ERROR;
368				/* XXXUNLOCK */
369				break;
370			}
371			for (i=1 ; (i < ninputs) && (error == 0) ; i++) {
372				uio = dreq->dreq_inbuf[i].dmbuf_uio;
373				error = uiomove(cp, count, uio);
374				if (error == 0) {
375					xor2(dst, dst, cp, count);
376				}
377			}
378			if (error == 0) {
379				error = uiomove(dst, count, uio_out);
380			} else {
381				/* XXXLOCK */
382				dreq->dreq_error = error;
383				dreq->dreq_flags |= DMOVER_REQ_ERROR;
384				/* XXXUNLOCK */
385				break;
386			}
387		}
388		break;
389	    }
390
391	default:
392		/* XXXLOCK */
393		dreq->dreq_error = EINVAL;
394		dreq->dreq_flags |= DMOVER_REQ_ERROR;
395		/* XXXUNLOCK */
396	}
397
398 done:
399	dmover_done(dreq);
400}
401
402/*
403 * swdmover_func_copy_process:
404 *
405 *	Processing routine for the "copy" function.
406 */
407static void
408swdmover_func_copy_process(struct dmover_request *dreq)
409{
410
411	/* XXX Currently, both buffers must be of same type. */
412	if (dreq->dreq_inbuf_type != dreq->dreq_outbuf_type) {
413		/* XXXLOCK */
414		dreq->dreq_error = EINVAL;
415		dreq->dreq_flags |= DMOVER_REQ_ERROR;
416		/* XXXUNLOCK */
417		goto done;
418	}
419
420	switch (dreq->dreq_outbuf_type) {
421	case DMOVER_BUF_LINEAR:
422		if (dreq->dreq_outbuf.dmbuf_linear.l_len !=
423		    dreq->dreq_inbuf[0].dmbuf_linear.l_len) {
424			/* XXXLOCK */
425			dreq->dreq_error = EINVAL;
426			dreq->dreq_flags |= DMOVER_REQ_ERROR;
427			/* XXXUNLOCK */
428			break;
429		}
430		memcpy(dreq->dreq_outbuf.dmbuf_linear.l_addr,
431		    dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
432		    dreq->dreq_outbuf.dmbuf_linear.l_len);
433		break;
434
435	case DMOVER_BUF_UIO:
436	    {
437		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
438		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
439		char cp[1024];
440		size_t count, buflen;
441		int error;
442
443		if (uio_in->uio_rw != UIO_WRITE ||
444		    uio_out->uio_rw != UIO_READ ||
445		    uio_in->uio_resid != uio_out->uio_resid) {
446			/* XXXLOCK */
447			dreq->dreq_error = EINVAL;
448			dreq->dreq_flags |= DMOVER_REQ_ERROR;
449			/* XXXUNLOCK */
450			break;
451		}
452
453		buflen = uio_in->uio_resid;
454		if (buflen > sizeof(cp))
455			buflen = sizeof(cp);
456
457		while ((count = uio_in->uio_resid) != 0) {
458			if (count > buflen)
459				count = buflen;
460			error = uiomove(cp, count, uio_in);
461			if (error == 0)
462				error = uiomove(cp, count, uio_out);
463			if (error) {
464				/* XXXLOCK */
465				dreq->dreq_error = error;
466				dreq->dreq_flags |= DMOVER_REQ_ERROR;
467				/* XXXUNLOCK */
468				break;
469			}
470		}
471		break;
472	    }
473
474	default:
475		/* XXXLOCK */
476		dreq->dreq_error = EINVAL;
477		dreq->dreq_flags |= DMOVER_REQ_ERROR;
478		/* XXXUNLOCK */
479	}
480
481 done:
482	dmover_done(dreq);
483}
484
485static const uint32_t iscsi_crc32c_table[256] = {
486	0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,
487	0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
488	0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
489	0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
490	0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,
491	0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
492	0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,
493	0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
494	0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
495	0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
496	0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,
497	0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
498	0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,
499	0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
500	0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
501	0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
502	0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,
503	0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
504	0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,
505	0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
506	0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
507	0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
508	0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,
509	0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
510	0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,
511	0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
512	0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
513	0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
514	0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,
515	0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
516	0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,
517	0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
518	0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
519	0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
520	0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,
521	0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
522	0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,
523	0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
524	0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
525	0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
526	0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,
527	0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
528	0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,
529	0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
530	0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
531	0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
532	0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,
533	0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
534	0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,
535	0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
536	0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
537	0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
538	0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,
539	0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
540	0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,
541	0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
542	0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
543	0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
544	0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,
545	0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
546	0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,
547	0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
548	0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
549	0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
550};
551
552static uint32_t
553iscsi_crc32c(const uint8_t *buf, size_t len, uint32_t last)
554{
555	uint32_t crc = 0xffffffffU ^ last;
556
557	while (len--)
558		crc = iscsi_crc32c_table[(crc ^ *buf++) & 0xff] ^ (crc >> 8);
559
560	return (crc ^ 0xffffffffU);
561}
562
563/*
564 * swdmover_func_iscsi_crc32c_process:
565 *
566 *	Processing routine for the "iscsi-crc32c" function.
567 */
568static void
569swdmover_func_iscsi_crc32c_process(struct dmover_request *dreq)
570{
571	uint32_t result;
572
573	/* No output buffer; we use the immediate only. */
574	if (dreq->dreq_outbuf_type != DMOVER_BUF_NONE) {
575		/* XXXLOCK */
576		dreq->dreq_error = EINVAL;
577		dreq->dreq_flags |= DMOVER_REQ_ERROR;
578		/* XXXUNLOCK */
579		goto done;
580	}
581
582	memcpy(&result, dreq->dreq_immediate, sizeof(result));
583
584	switch (dreq->dreq_inbuf_type) {
585	case DMOVER_BUF_LINEAR:
586		result = iscsi_crc32c(dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
587		    dreq->dreq_inbuf[0].dmbuf_linear.l_len, result);
588		break;
589
590	case DMOVER_BUF_UIO:
591	    {
592		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
593		uint8_t cp[1024];
594		size_t count, buflen;
595		int error;
596
597		if (uio_in->uio_rw != UIO_WRITE) {
598			/* XXXLOCK */
599			dreq->dreq_error = EINVAL;
600			dreq->dreq_flags |= DMOVER_REQ_ERROR;
601			/* XXXUNLOCK */
602			goto done;
603		}
604
605		buflen = uio_in->uio_resid;
606		if (buflen > sizeof(cp))
607			buflen = sizeof(cp);
608
609		while ((count = uio_in->uio_resid) != 0) {
610			if (count > buflen)
611				count = buflen;
612			error = uiomove(cp, count, uio_in);
613			if (error) {
614				/* XXXLOCK */
615				dreq->dreq_error = error;
616				dreq->dreq_flags |= DMOVER_REQ_ERROR;
617				/* XXXUNLOCK */
618				goto done;
619			} else
620				result = iscsi_crc32c(cp, count, result);
621		}
622		break;
623	    }
624
625	default:
626		/* XXXLOCK */
627		dreq->dreq_error = EINVAL;
628		dreq->dreq_flags |= DMOVER_REQ_ERROR;
629		/* XXXUNLOCK */
630		goto done;
631	}
632
633	memcpy(dreq->dreq_immediate, &result, sizeof(result));
634 done:
635	dmover_done(dreq);
636}
637
638static struct swdmover_function swdmover_func_zero = {
639	swdmover_func_zero_process
640};
641
642static struct swdmover_function swdmover_func_fill8 = {
643	swdmover_func_fill8_process
644};
645
646static struct swdmover_function swdmover_func_copy = {
647	swdmover_func_copy_process
648};
649
650static struct swdmover_function swdmover_func_xor = {
651	swdmover_func_xor_process
652};
653
654static struct swdmover_function swdmover_func_iscsi_crc32c = {
655	swdmover_func_iscsi_crc32c_process
656};
657
658const struct dmover_algdesc swdmover_algdescs[] = {
659	{
660	  DMOVER_FUNC_XOR2,
661	  &swdmover_func_xor,
662	  2
663	},
664	{
665	  DMOVER_FUNC_XOR3,
666	  &swdmover_func_xor,
667	  3
668	},
669	{
670	  DMOVER_FUNC_XOR4,
671	  &swdmover_func_xor,
672	  4
673	},
674	{
675	  DMOVER_FUNC_XOR5,
676	  &swdmover_func_xor,
677	  5
678	},
679	{
680	  DMOVER_FUNC_XOR6,
681	  &swdmover_func_xor,
682	  6
683	},
684	{
685	  DMOVER_FUNC_XOR7,
686	  &swdmover_func_xor,
687	  7
688	},
689	{
690	  DMOVER_FUNC_XOR8,
691	  &swdmover_func_xor,
692	  8
693	},
694	{
695	  DMOVER_FUNC_ZERO,
696	  &swdmover_func_zero,
697	  0
698	},
699	{
700	  DMOVER_FUNC_FILL8,
701	  &swdmover_func_fill8,
702	  0
703	},
704	{
705	  DMOVER_FUNC_COPY,
706	  &swdmover_func_copy,
707	  1
708	},
709	{
710	  DMOVER_FUNC_ISCSI_CRC32C,
711	  &swdmover_func_iscsi_crc32c,
712	  1,
713	},
714};
715#define	SWDMOVER_ALGDESC_COUNT \
716	(sizeof(swdmover_algdescs) / sizeof(swdmover_algdescs[0]))
717
718/*
719 * swdmoverattach:
720 *
721 *	Pesudo-device attach routine.
722 */
723void
724swdmoverattach(int count)
725{
726	int error;
727
728	swdmover_backend.dmb_name = "swdmover";
729	swdmover_backend.dmb_speed = 1;		/* XXX */
730	swdmover_backend.dmb_cookie = NULL;
731	swdmover_backend.dmb_algdescs = swdmover_algdescs;
732	swdmover_backend.dmb_nalgdescs = SWDMOVER_ALGDESC_COUNT;
733	swdmover_backend.dmb_process = swdmover_process;
734
735	error = kthread_create(PRI_NONE, 0, NULL, swdmover_thread,
736	    &swdmover_backend, &swdmover_lwp, "swdmover");
737	if (error)
738		printf("WARNING: unable to create swdmover thread, "
739		    "error = %d\n", error);
740
741	/* XXX Should only register this when kthread creation succeeds. */
742	dmover_backend_register(&swdmover_backend);
743}
744