Home | History | Annotate | Line # | Download | only in dmover
swdmover.c revision 1.5
      1 /*	$NetBSD: swdmover.c,v 1.5 2003/07/19 02:05:35 thorpej 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.5 2003/07/19 02:05:35 thorpej Exp $");
     53 
     54 #include <sys/param.h>
     55 #include <sys/lock.h>
     56 #include <sys/kthread.h>
     57 #include <sys/systm.h>
     58 #include <sys/uio.h>
     59 
     60 #include <dev/dmover/dmovervar.h>
     61 
     62 struct swdmover_function {
     63 	void	(*sdf_process)(struct dmover_request *);
     64 };
     65 
     66 static struct dmover_backend swdmover_backend;
     67 static struct proc *swdmover_proc;
     68 static int swdmover_cv;
     69 
     70 void	swdmoverattach(int);
     71 
     72 /*
     73  * swdmover_process:
     74  *
     75  *	Dmover back-end entry point.
     76  */
     77 static void
     78 swdmover_process(struct dmover_backend *dmb)
     79 {
     80 	int s;
     81 
     82 	/*
     83 	 * Just wake up the processing thread.  This will allow
     84 	 * requests to linger on the middle-end's queue so that
     85 	 * they can be cancelled, if need-be.
     86 	 */
     87 	s = splbio();
     88 	/* XXXLOCK */
     89 	if (TAILQ_EMPTY(&dmb->dmb_pendreqs) == 0)
     90 		wakeup(&swdmover_cv);
     91 	/* XXXUNLOCK */
     92 	splx(s);
     93 }
     94 
     95 /*
     96  * swdmover_thread:
     97  *
     98  *	Request processing thread.
     99  */
    100 static void
    101 swdmover_thread(void *arg)
    102 {
    103 	struct dmover_backend *dmb = arg;
    104 	struct dmover_request *dreq;
    105 	struct swdmover_function *sdf;
    106 	int s;
    107 
    108 	s = splbio();
    109 	/* XXXLOCK */
    110 
    111 	for (;;) {
    112 		dreq = TAILQ_FIRST(&dmb->dmb_pendreqs);
    113 		if (dreq == NULL) {
    114 			/* XXXUNLOCK */
    115 			(void) tsleep(&swdmover_cv, PRIBIO, "swdmvr", 0);
    116 			continue;
    117 		}
    118 
    119 		dmover_backend_remque(dmb, dreq);
    120 		dreq->dreq_flags |= DMOVER_REQ_RUNNING;
    121 
    122 		/* XXXUNLOCK */
    123 		splx(s);
    124 
    125 		sdf = dreq->dreq_assignment->das_algdesc->dad_data;
    126 		(*sdf->sdf_process)(dreq);
    127 
    128 		s = splbio();
    129 		/* XXXLOCK */
    130 	}
    131 }
    132 
    133 /*
    134  * swdmover_func_zero_process:
    135  *
    136  *	Processing routine for the "zero" function.
    137  */
    138 static void
    139 swdmover_func_zero_process(struct dmover_request *dreq)
    140 {
    141 
    142 	switch (dreq->dreq_outbuf_type) {
    143 	case DMOVER_BUF_LINEAR:
    144 		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr, 0,
    145 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    146 		break;
    147 
    148 	case DMOVER_BUF_UIO:
    149 	    {
    150 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
    151 		char *cp;
    152 		size_t count, buflen;
    153 		int error;
    154 
    155 		if (uio->uio_rw != UIO_READ) {
    156 			/* XXXLOCK */
    157 			dreq->dreq_error = EINVAL;
    158 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    159 			/* XXXUNLOCK */
    160 			break;
    161 		}
    162 
    163 		buflen = uio->uio_resid;
    164 		if (buflen > 1024)
    165 			buflen = 1024;
    166 		cp = alloca(buflen);
    167 		memset(cp, 0, buflen);
    168 
    169 		while ((count = uio->uio_resid) != 0) {
    170 			if (count > buflen)
    171 				count = buflen;
    172 			error = uiomove(cp, count, uio);
    173 			if (error) {
    174 				/* XXXLOCK */
    175 				dreq->dreq_error = error;
    176 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    177 				/* XXXUNLOCK */
    178 				break;
    179 			}
    180 		}
    181 		break;
    182 	    }
    183 
    184 	default:
    185 		/* XXXLOCK */
    186 		dreq->dreq_error = EINVAL;
    187 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    188 		/* XXXUNLOCK */
    189 	}
    190 
    191 	dmover_done(dreq);
    192 }
    193 
    194 /*
    195  * swdmover_func_fill8_process:
    196  *
    197  *	Processing routine for the "fill8" function.
    198  */
    199 static void
    200 swdmover_func_fill8_process(struct dmover_request *dreq)
    201 {
    202 
    203 	switch (dreq->dreq_outbuf_type) {
    204 	case DMOVER_BUF_LINEAR:
    205 		memset(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    206 		    dreq->dreq_immediate[0],
    207 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    208 		break;
    209 
    210 	case DMOVER_BUF_UIO:
    211 	    {
    212 		struct uio *uio = dreq->dreq_outbuf.dmbuf_uio;
    213 		char *cp;
    214 		size_t count, buflen;
    215 		int error;
    216 
    217 		if (uio->uio_rw != UIO_READ) {
    218 			/* XXXLOCK */
    219 			dreq->dreq_error = EINVAL;
    220 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    221 			/* XXXUNLOCK */
    222 			break;
    223 		}
    224 
    225 		buflen = uio->uio_resid;
    226 		if (buflen > 1024)
    227 			buflen = 1024;
    228 		cp = alloca(buflen);
    229 		memset(cp, dreq->dreq_immediate[0], buflen);
    230 
    231 		while ((count = uio->uio_resid) != 0) {
    232 			if (count > buflen)
    233 				count = buflen;
    234 			error = uiomove(cp, count, uio);
    235 			if (error) {
    236 				/* XXXLOCK */
    237 				dreq->dreq_error = error;
    238 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    239 				/* XXXUNLOCK */
    240 				break;
    241 			}
    242 		}
    243 		break;
    244 	    }
    245 
    246 	default:
    247 		/* XXXLOCK */
    248 		dreq->dreq_error = EINVAL;
    249 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    250 		/* XXXUNLOCK */
    251 	}
    252 
    253 	dmover_done(dreq);
    254 }
    255 
    256 /*
    257  * swdmover_func_copy_process:
    258  *
    259  *	Processing routine for the "copy" function.
    260  */
    261 static void
    262 swdmover_func_copy_process(struct dmover_request *dreq)
    263 {
    264 
    265 	/* XXX Currently, both buffers must be of same type. */
    266 	if (dreq->dreq_inbuf_type != dreq->dreq_outbuf_type) {
    267 		/* XXXLOCK */
    268 		dreq->dreq_error = EINVAL;
    269 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    270 		/* XXXUNLOCK */
    271 		goto done;
    272 	}
    273 
    274 	switch (dreq->dreq_outbuf_type) {
    275 	case DMOVER_BUF_LINEAR:
    276 		if (dreq->dreq_outbuf.dmbuf_linear.l_len !=
    277 		    dreq->dreq_inbuf[0].dmbuf_linear.l_len) {
    278 			/* XXXLOCK */
    279 			dreq->dreq_error = EINVAL;
    280 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    281 			/* XXXUNLOCK */
    282 			break;
    283 		}
    284 		memcpy(dreq->dreq_outbuf.dmbuf_linear.l_addr,
    285 		    dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
    286 		    dreq->dreq_outbuf.dmbuf_linear.l_len);
    287 		break;
    288 
    289 	case DMOVER_BUF_UIO:
    290 	    {
    291 		struct uio *uio_out = dreq->dreq_outbuf.dmbuf_uio;
    292 		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
    293 		char *cp;
    294 		size_t count, buflen;
    295 		int error;
    296 
    297 		if (uio_in->uio_rw != UIO_WRITE ||
    298 		    uio_out->uio_rw != UIO_READ ||
    299 		    uio_in->uio_resid != uio_out->uio_resid) {
    300 			/* XXXLOCK */
    301 			dreq->dreq_error = EINVAL;
    302 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    303 			/* XXXUNLOCK */
    304 			break;
    305 		}
    306 
    307 		buflen = uio_in->uio_resid;
    308 		if (buflen > 1024)
    309 			buflen = 1024;
    310 		cp = alloca(buflen);
    311 
    312 		while ((count = uio_in->uio_resid) != 0) {
    313 			if (count > buflen)
    314 				count = buflen;
    315 			error = uiomove(cp, count, uio_in);
    316 			if (error == 0)
    317 				error = uiomove(cp, count, uio_out);
    318 			if (error) {
    319 				/* XXXLOCK */
    320 				dreq->dreq_error = error;
    321 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    322 				/* XXXUNLOCK */
    323 				break;
    324 			}
    325 		}
    326 		break;
    327 	    }
    328 
    329 	default:
    330 		/* XXXLOCK */
    331 		dreq->dreq_error = EINVAL;
    332 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    333 		/* XXXUNLOCK */
    334 	}
    335 
    336  done:
    337 	dmover_done(dreq);
    338 }
    339 
    340 static const uint32_t iscsi_crc32c_table[256] = {
    341 	0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,
    342 	0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
    343 	0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
    344 	0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
    345 	0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,
    346 	0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
    347 	0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,
    348 	0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
    349 	0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
    350 	0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
    351 	0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,
    352 	0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
    353 	0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,
    354 	0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
    355 	0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
    356 	0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
    357 	0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,
    358 	0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
    359 	0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,
    360 	0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
    361 	0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
    362 	0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
    363 	0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,
    364 	0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
    365 	0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,
    366 	0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
    367 	0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
    368 	0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
    369 	0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,
    370 	0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
    371 	0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,
    372 	0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
    373 	0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
    374 	0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
    375 	0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,
    376 	0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
    377 	0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,
    378 	0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
    379 	0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
    380 	0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
    381 	0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,
    382 	0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
    383 	0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,
    384 	0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
    385 	0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
    386 	0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
    387 	0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,
    388 	0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
    389 	0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,
    390 	0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
    391 	0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
    392 	0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
    393 	0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,
    394 	0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
    395 	0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,
    396 	0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
    397 	0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
    398 	0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
    399 	0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,
    400 	0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
    401 	0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,
    402 	0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
    403 	0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
    404 	0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
    405 };
    406 
    407 static uint32_t
    408 iscsi_crc32c(const uint8_t *buf, size_t len, uint32_t last)
    409 {
    410 	uint32_t crc = 0xffffffffU ^ last;
    411 
    412 	while (len--)
    413 		crc = iscsi_crc32c_table[(crc ^ *buf++) & 0xff] ^ (crc >> 8);
    414 
    415 	return (crc ^ 0xffffffffU);
    416 }
    417 
    418 /*
    419  * swdmover_func_iscsi_crc32c_process:
    420  *
    421  *	Processing routine for the "iscsi-crc32c" function.
    422  */
    423 static void
    424 swdmover_func_iscsi_crc32c_process(struct dmover_request *dreq)
    425 {
    426 	uint32_t result;
    427 
    428 	/* No output buffer; we use the immediate only. */
    429 	if (dreq->dreq_outbuf_type != DMOVER_BUF_NONE) {
    430 		/* XXXLOCK */
    431 		dreq->dreq_error = EINVAL;
    432 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    433 		/* XXXUNLOCK */
    434 		goto done;
    435 	}
    436 
    437 	memcpy(&result, dreq->dreq_immediate, sizeof(result));
    438 
    439 	switch (dreq->dreq_inbuf_type) {
    440 	case DMOVER_BUF_LINEAR:
    441 		result = iscsi_crc32c(dreq->dreq_inbuf[0].dmbuf_linear.l_addr,
    442 		    dreq->dreq_inbuf[0].dmbuf_linear.l_len, result);
    443 		break;
    444 
    445 	case DMOVER_BUF_UIO:
    446 	    {
    447 		struct uio *uio_in = dreq->dreq_inbuf[0].dmbuf_uio;
    448 		uint8_t *cp;
    449 		size_t count, buflen;
    450 		int error;
    451 
    452 		if (uio_in->uio_rw != UIO_WRITE) {
    453 			/* XXXLOCK */
    454 			dreq->dreq_error = EINVAL;
    455 			dreq->dreq_flags |= DMOVER_REQ_ERROR;
    456 			/* XXXUNLOCK */
    457 			goto done;
    458 		}
    459 
    460 		buflen = uio_in->uio_resid;
    461 		if (buflen > 1024)
    462 			buflen = 1024;
    463 		cp = alloca(buflen);
    464 
    465 		while ((count = uio_in->uio_resid) != 0) {
    466 			if (count > buflen)
    467 				count = buflen;
    468 			error = uiomove(cp, count, uio_in);
    469 			if (error) {
    470 				/* XXXLOCK */
    471 				dreq->dreq_error = error;
    472 				dreq->dreq_flags |= DMOVER_REQ_ERROR;
    473 				/* XXXUNLOCK */
    474 				goto done;
    475 			} else
    476 				result = iscsi_crc32c(cp, count, result);
    477 		}
    478 		break;
    479 	    }
    480 
    481 	default:
    482 		/* XXXLOCK */
    483 		dreq->dreq_error = EINVAL;
    484 		dreq->dreq_flags |= DMOVER_REQ_ERROR;
    485 		/* XXXUNLOCK */
    486 		goto done;
    487 	}
    488 
    489 	memcpy(dreq->dreq_immediate, &result, sizeof(result));
    490  done:
    491 	dmover_done(dreq);
    492 }
    493 
    494 static struct swdmover_function swdmover_func_zero = {
    495 	swdmover_func_zero_process
    496 };
    497 
    498 static struct swdmover_function swdmover_func_fill8 = {
    499 	swdmover_func_fill8_process
    500 };
    501 
    502 static struct swdmover_function swdmover_func_copy = {
    503 	swdmover_func_copy_process
    504 };
    505 
    506 static struct swdmover_function swdmover_func_iscsi_crc32c = {
    507 	swdmover_func_iscsi_crc32c_process
    508 };
    509 
    510 const struct dmover_algdesc swdmover_algdescs[] = {
    511 	{
    512 	  DMOVER_FUNC_ZERO,
    513 	  &swdmover_func_zero,
    514 	  0
    515 	},
    516 	{
    517 	  DMOVER_FUNC_FILL8,
    518 	  &swdmover_func_fill8,
    519 	  0
    520 	},
    521 	{
    522 	  DMOVER_FUNC_COPY,
    523 	  &swdmover_func_copy,
    524 	  1
    525 	},
    526 	{
    527 	  DMOVER_FUNC_ISCSI_CRC32C,
    528 	  &swdmover_func_iscsi_crc32c,
    529 	  1,
    530 	},
    531 };
    532 #define	SWDMOVER_ALGDESC_COUNT \
    533 	(sizeof(swdmover_algdescs) / sizeof(swdmover_algdescs[0]))
    534 
    535 /*
    536  * swdmover_create_thread:
    537  *
    538  *	Actually create the swdmover processing thread.
    539  */
    540 static void
    541 swdmover_create_thread(void *arg)
    542 {
    543 	int error;
    544 
    545 	error = kthread_create1(swdmover_thread, arg, &swdmover_proc,
    546 	    "swdmover");
    547 	if (error)
    548 		printf("WARNING: unable to create swdmover thread, "
    549 		    "error = %d\n", error);
    550 }
    551 
    552 /*
    553  * swdmoverattach:
    554  *
    555  *	Pesudo-device attach routine.
    556  */
    557 void
    558 swdmoverattach(int count)
    559 {
    560 
    561 	swdmover_backend.dmb_name = "swdmover";
    562 	swdmover_backend.dmb_speed = 1;		/* XXX */
    563 	swdmover_backend.dmb_cookie = NULL;
    564 	swdmover_backend.dmb_algdescs = swdmover_algdescs;
    565 	swdmover_backend.dmb_nalgdescs = SWDMOVER_ALGDESC_COUNT;
    566 	swdmover_backend.dmb_process = swdmover_process;
    567 
    568 	kthread_create(swdmover_create_thread, &swdmover_backend);
    569 
    570 	/* XXX Should only register this when kthread creation succeeds. */
    571 	dmover_backend_register(&swdmover_backend);
    572 }
    573