1*7991f5a7Sandvar /* $NetBSD: dm_target_flakey.c,v 1.4 2021/07/24 21:31:37 andvar Exp $ */
28dab45b6Stkusumi
38dab45b6Stkusumi /*
48dab45b6Stkusumi * Copyright (c) 2020 The NetBSD Foundation, Inc.
505e54a62Stkusumi * Copyright (c) 2015 The DragonFly Project. All rights reserved.
68dab45b6Stkusumi * All rights reserved.
78dab45b6Stkusumi *
88dab45b6Stkusumi * This code is derived from software contributed to The NetBSD Foundation
98dab45b6Stkusumi * by Tomohiro Kusumi <tkusumi@netbsd.org>.
108dab45b6Stkusumi *
118dab45b6Stkusumi * Redistribution and use in source and binary forms, with or without
128dab45b6Stkusumi * modification, are permitted provided that the following conditions
138dab45b6Stkusumi * are met:
148dab45b6Stkusumi * 1. Redistributions of source code must retain the above copyright
158dab45b6Stkusumi * notice, this list of conditions and the following disclaimer.
168dab45b6Stkusumi * 2. Redistributions in binary form must reproduce the above copyright
178dab45b6Stkusumi * notice, this list of conditions and the following disclaimer in the
188dab45b6Stkusumi * documentation and/or other materials provided with the distribution.
198dab45b6Stkusumi *
208dab45b6Stkusumi * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
218dab45b6Stkusumi * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
228dab45b6Stkusumi * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
238dab45b6Stkusumi * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
248dab45b6Stkusumi * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
258dab45b6Stkusumi * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
268dab45b6Stkusumi * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
278dab45b6Stkusumi * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
288dab45b6Stkusumi * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
298dab45b6Stkusumi * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
308dab45b6Stkusumi * POSSIBILITY OF SUCH DAMAGE.
318dab45b6Stkusumi */
328dab45b6Stkusumi #include <sys/cdefs.h>
33*7991f5a7Sandvar __KERNEL_RCSID(0, "$NetBSD: dm_target_flakey.c,v 1.4 2021/07/24 21:31:37 andvar Exp $");
348dab45b6Stkusumi
358dab45b6Stkusumi #include <sys/types.h>
368dab45b6Stkusumi #include <sys/param.h>
378dab45b6Stkusumi #include <sys/kernel.h>
388dab45b6Stkusumi #include <sys/buf.h>
398dab45b6Stkusumi #include <sys/kmem.h>
408dab45b6Stkusumi
418dab45b6Stkusumi #include "dm.h"
428dab45b6Stkusumi
438dab45b6Stkusumi //#define DEBUG_FLAKEY
448dab45b6Stkusumi //#define HAS_BUF_PRIV2 /* XXX requires nonexistent buf::b_private2. */
458dab45b6Stkusumi
468dab45b6Stkusumi typedef struct target_flakey_config {
478dab45b6Stkusumi dm_pdev_t *pdev;
488dab45b6Stkusumi uint64_t offset;
498dab45b6Stkusumi int up_int;
508dab45b6Stkusumi int down_int;
518dab45b6Stkusumi int offset_time; /* XXX "tick" in hz(9) not working. */
528dab45b6Stkusumi
538dab45b6Stkusumi /* drop_writes feature */
548dab45b6Stkusumi int drop_writes;
558dab45b6Stkusumi
568dab45b6Stkusumi /* corrupt_bio_byte feature */
578dab45b6Stkusumi unsigned int corrupt_buf_byte;
588dab45b6Stkusumi unsigned int corrupt_buf_rw;
598dab45b6Stkusumi unsigned int corrupt_buf_value;
608dab45b6Stkusumi unsigned int corrupt_buf_flags; /* for B_XXX flags */
618dab45b6Stkusumi } dm_target_flakey_config_t;
628dab45b6Stkusumi
638dab45b6Stkusumi #define BUF_CMD_READ 1
648dab45b6Stkusumi #define BUF_CMD_WRITE 2
658dab45b6Stkusumi
668dab45b6Stkusumi #define FLAKEY_CORRUPT_DIR(tfc) \
678dab45b6Stkusumi ((tfc)->corrupt_buf_rw == BUF_CMD_READ ? 'r' : 'w')
688dab45b6Stkusumi
698dab45b6Stkusumi static int _init_features(dm_target_flakey_config_t*, int, char**);
708dab45b6Stkusumi static __inline void _submit(dm_target_flakey_config_t*, struct buf*);
718dab45b6Stkusumi static int _flakey_read(dm_target_flakey_config_t*, struct buf*);
728dab45b6Stkusumi static int _flakey_write(dm_target_flakey_config_t*, struct buf*);
738dab45b6Stkusumi static int _flakey_corrupt_buf(dm_target_flakey_config_t*, struct buf*);
748dab45b6Stkusumi
758dab45b6Stkusumi #ifdef DM_TARGET_MODULE
768dab45b6Stkusumi /*
778dab45b6Stkusumi * Every target can be compiled directly to dm driver or as a
788dab45b6Stkusumi * separate module this part of target is used for loading targets
798dab45b6Stkusumi * to dm driver.
808dab45b6Stkusumi * Target can be unloaded from kernel only if there are no users of
818dab45b6Stkusumi * it e.g. there are no devices which uses that target.
828dab45b6Stkusumi */
838dab45b6Stkusumi #include <sys/kernel.h>
848dab45b6Stkusumi #include <sys/module.h>
858dab45b6Stkusumi
868dab45b6Stkusumi MODULE(MODULE_CLASS_MISC, dm_target_flakey, NULL);
878dab45b6Stkusumi
888dab45b6Stkusumi static int
dm_target_flakey_modcmd(modcmd_t cmd,void * arg)898dab45b6Stkusumi dm_target_flakey_modcmd(modcmd_t cmd, void *arg)
908dab45b6Stkusumi {
918dab45b6Stkusumi dm_target_t *dmt;
928dab45b6Stkusumi int r;
938dab45b6Stkusumi
948dab45b6Stkusumi switch (cmd) {
958dab45b6Stkusumi case MODULE_CMD_INIT:
968dab45b6Stkusumi if ((dmt = dm_target_lookup("flakey")) != NULL) {
978dab45b6Stkusumi dm_target_unbusy(dmt);
988dab45b6Stkusumi return EEXIST;
998dab45b6Stkusumi }
1008dab45b6Stkusumi dmt = dm_target_alloc("flakey");
1018dab45b6Stkusumi
1028dab45b6Stkusumi dmt->version[0] = 1;
1038dab45b6Stkusumi dmt->version[1] = 0;
1048dab45b6Stkusumi dmt->version[2] = 0;
1058dab45b6Stkusumi dmt->init = &dm_target_flakey_init;
1068dab45b6Stkusumi dmt->table = &dm_target_flakey_table;
1078dab45b6Stkusumi dmt->strategy = &dm_target_flakey_strategy;
1088dab45b6Stkusumi dmt->sync = &dm_target_flakey_sync;
1098dab45b6Stkusumi dmt->destroy = &dm_target_flakey_destroy;
11067de4a93Stkusumi //dmt->upcall = &dm_target_flakey_upcall;
1118dab45b6Stkusumi dmt->secsize = &dm_target_flakey_secsize;
1128dab45b6Stkusumi
1138dab45b6Stkusumi r = dm_target_insert(dmt);
1148dab45b6Stkusumi
1158dab45b6Stkusumi break;
1168dab45b6Stkusumi
1178dab45b6Stkusumi case MODULE_CMD_FINI:
1188dab45b6Stkusumi r = dm_target_rem("flakey");
1198dab45b6Stkusumi break;
1208dab45b6Stkusumi
1218dab45b6Stkusumi case MODULE_CMD_STAT:
1228dab45b6Stkusumi return ENOTTY;
1238dab45b6Stkusumi
1248dab45b6Stkusumi default:
1258dab45b6Stkusumi return ENOTTY;
1268dab45b6Stkusumi }
1278dab45b6Stkusumi
1288dab45b6Stkusumi return r;
1298dab45b6Stkusumi }
1308dab45b6Stkusumi #endif
1318dab45b6Stkusumi
1328dab45b6Stkusumi int
dm_target_flakey_init(dm_table_entry_t * table_en,int argc,char ** argv)1338dab45b6Stkusumi dm_target_flakey_init(dm_table_entry_t *table_en, int argc, char **argv)
1348dab45b6Stkusumi {
1358dab45b6Stkusumi dm_target_flakey_config_t *tfc;
1368dab45b6Stkusumi dm_pdev_t *dmp;
1378dab45b6Stkusumi int err;
1388dab45b6Stkusumi
1398dab45b6Stkusumi if (argc < 4) {
1408dab45b6Stkusumi printf("Flakey target takes at least 4 args, %d given\n", argc);
1418dab45b6Stkusumi return EINVAL;
1428dab45b6Stkusumi }
1438dab45b6Stkusumi
1448dab45b6Stkusumi aprint_debug("Flakey target init function called: argc=%d\n", argc);
1458dab45b6Stkusumi
1468dab45b6Stkusumi /* Insert dmp to global pdev list */
1478dab45b6Stkusumi if ((dmp = dm_pdev_insert(argv[0])) == NULL)
1488dab45b6Stkusumi return ENOENT;
1498dab45b6Stkusumi
1508dab45b6Stkusumi tfc = kmem_alloc(sizeof(dm_target_flakey_config_t), KM_SLEEP);
1518dab45b6Stkusumi tfc->pdev = dmp;
1528dab45b6Stkusumi tfc->offset = atoi64(argv[1]);
1538dab45b6Stkusumi tfc->up_int = atoi64(argv[2]);
1548dab45b6Stkusumi tfc->down_int = atoi64(argv[3]);
1558dab45b6Stkusumi tfc->offset_time = tick;
1568dab45b6Stkusumi
1578dab45b6Stkusumi if ((tfc->up_int + tfc->down_int) == 0) {
1588dab45b6Stkusumi printf("Sum of up/down interval is 0\n");
1598dab45b6Stkusumi err = EINVAL;
1608dab45b6Stkusumi goto fail;
1618dab45b6Stkusumi }
1628dab45b6Stkusumi
1638dab45b6Stkusumi if (tfc->up_int + tfc->down_int < tfc->up_int) {
1648dab45b6Stkusumi printf("Interval time overflow\n");
1658dab45b6Stkusumi err = EINVAL;
1668dab45b6Stkusumi goto fail;
1678dab45b6Stkusumi }
1688dab45b6Stkusumi
1698dab45b6Stkusumi err = _init_features(tfc, argc - 4, argv + 4);
1708dab45b6Stkusumi if (err)
1718dab45b6Stkusumi goto fail;
1728dab45b6Stkusumi
1738dab45b6Stkusumi dm_table_add_deps(table_en, dmp);
1748dab45b6Stkusumi table_en->target_config = tfc;
1758dab45b6Stkusumi
1768dab45b6Stkusumi return 0;
1778dab45b6Stkusumi fail:
1788dab45b6Stkusumi kmem_free(tfc, sizeof(*tfc));
1798dab45b6Stkusumi return err;
1808dab45b6Stkusumi }
1818dab45b6Stkusumi
1828dab45b6Stkusumi static int
_init_features(dm_target_flakey_config_t * tfc,int argc,char ** argv)1838dab45b6Stkusumi _init_features(dm_target_flakey_config_t *tfc, int argc, char **argv)
1848dab45b6Stkusumi {
1858dab45b6Stkusumi char *arg;
1868dab45b6Stkusumi unsigned int value;
1878dab45b6Stkusumi
1888dab45b6Stkusumi if (argc == 0)
1898dab45b6Stkusumi return 0;
1908dab45b6Stkusumi
1918dab45b6Stkusumi argc = atoi64(*argv++); /* # of args for features */
1928dab45b6Stkusumi if (argc > 6) {
1938dab45b6Stkusumi printf("Invalid # of feature args %d\n", argc);
1948dab45b6Stkusumi return EINVAL;
1958dab45b6Stkusumi }
1968dab45b6Stkusumi
1978dab45b6Stkusumi while (argc) {
1988dab45b6Stkusumi argc--;
1998dab45b6Stkusumi arg = *argv++;
2008dab45b6Stkusumi
2018dab45b6Stkusumi /* drop_writes */
2028dab45b6Stkusumi if (strcmp(arg, "drop_writes") == 0) {
2038dab45b6Stkusumi tfc->drop_writes = 1;
2048dab45b6Stkusumi continue;
2058dab45b6Stkusumi }
2068dab45b6Stkusumi
2078dab45b6Stkusumi /* corrupt_bio_byte <Nth_byte> <direction> <value> <flags> */
2088dab45b6Stkusumi if (strcmp(arg, "corrupt_bio_byte") == 0) {
2098dab45b6Stkusumi if (argc < 4) {
2108dab45b6Stkusumi printf("Invalid # of feature args %d for "
2118dab45b6Stkusumi "corrupt_bio_byte\n", argc);
2128dab45b6Stkusumi return EINVAL;
2138dab45b6Stkusumi }
2148dab45b6Stkusumi
2158dab45b6Stkusumi /* <Nth_byte> */
2168dab45b6Stkusumi argc--;
2178dab45b6Stkusumi value = atoi64(*argv++);
2188dab45b6Stkusumi if (value < 1) {
2198dab45b6Stkusumi printf("Invalid corrupt_bio_byte "
2208dab45b6Stkusumi "<Nth_byte> arg %u\n", value);
2218dab45b6Stkusumi return EINVAL;
2228dab45b6Stkusumi }
2238dab45b6Stkusumi tfc->corrupt_buf_byte = value;
2248dab45b6Stkusumi
2258dab45b6Stkusumi /* <direction> */
2268dab45b6Stkusumi argc--;
2278dab45b6Stkusumi arg = *argv++;
2288dab45b6Stkusumi if (strcmp(arg, "r") == 0) {
2298dab45b6Stkusumi tfc->corrupt_buf_rw = BUF_CMD_READ;
2308dab45b6Stkusumi } else if (strcmp(arg, "w") == 0) {
2318dab45b6Stkusumi tfc->corrupt_buf_rw = BUF_CMD_WRITE;
2328dab45b6Stkusumi } else {
2338dab45b6Stkusumi printf("Invalid corrupt_bio_byte "
2348dab45b6Stkusumi "<direction> arg %s\n", arg);
2358dab45b6Stkusumi return EINVAL;
2368dab45b6Stkusumi }
2378dab45b6Stkusumi
2388dab45b6Stkusumi /* <value> */
2398dab45b6Stkusumi argc--;
2408dab45b6Stkusumi value = atoi64(*argv++);
2418dab45b6Stkusumi if (value > 0xff) {
2428dab45b6Stkusumi printf("Invalid corrupt_bio_byte "
2438dab45b6Stkusumi "<value> arg %u\n", value);
2448dab45b6Stkusumi return EINVAL;
2458dab45b6Stkusumi }
2468dab45b6Stkusumi tfc->corrupt_buf_value = value;
2478dab45b6Stkusumi
2488dab45b6Stkusumi /* <flags> */
2498dab45b6Stkusumi argc--;
2508dab45b6Stkusumi tfc->corrupt_buf_flags = atoi64(*argv++);
2518dab45b6Stkusumi
2528dab45b6Stkusumi continue;
2538dab45b6Stkusumi }
2548dab45b6Stkusumi
2558dab45b6Stkusumi printf("Unknown Flakey target feature %s\n", arg);
2568dab45b6Stkusumi return EINVAL;
2578dab45b6Stkusumi }
2588dab45b6Stkusumi
2598dab45b6Stkusumi if (tfc->drop_writes && (tfc->corrupt_buf_rw == BUF_CMD_WRITE)) {
2608dab45b6Stkusumi printf("Flakey target doesn't allow drop_writes feature and "
2618dab45b6Stkusumi "corrupt_bio_byte feature with 'w' set\n");
2628dab45b6Stkusumi return EINVAL;
2638dab45b6Stkusumi }
2648dab45b6Stkusumi
2658dab45b6Stkusumi return 0;
2668dab45b6Stkusumi }
2678dab45b6Stkusumi
2688dab45b6Stkusumi char *
dm_target_flakey_table(void * target_config)2698dab45b6Stkusumi dm_target_flakey_table(void *target_config)
2708dab45b6Stkusumi {
2718dab45b6Stkusumi dm_target_flakey_config_t *tfc;
2728dab45b6Stkusumi char *params, *p;
2738dab45b6Stkusumi int drop_writes;
2748dab45b6Stkusumi
2758dab45b6Stkusumi tfc = target_config;
2768dab45b6Stkusumi KASSERT(tfc != NULL);
2778dab45b6Stkusumi
2788dab45b6Stkusumi aprint_debug("Flakey target table function called\n");
2798dab45b6Stkusumi
2808dab45b6Stkusumi drop_writes = tfc->drop_writes;
2818dab45b6Stkusumi
2828dab45b6Stkusumi params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
2838dab45b6Stkusumi p = params;
2848dab45b6Stkusumi p += snprintf(p, DM_MAX_PARAMS_SIZE, "%s %d %d %d %u ",
2858dab45b6Stkusumi tfc->pdev->udev_name, tfc->offset_time,
2868dab45b6Stkusumi tfc->up_int, tfc->down_int,
2878dab45b6Stkusumi drop_writes + (tfc->corrupt_buf_byte > 0) * 5);
2888dab45b6Stkusumi
2898dab45b6Stkusumi if (drop_writes)
2908dab45b6Stkusumi p += snprintf(p, DM_MAX_PARAMS_SIZE, "drop_writes ");
2918dab45b6Stkusumi
2928dab45b6Stkusumi if (tfc->corrupt_buf_byte)
2938dab45b6Stkusumi p += snprintf(p, DM_MAX_PARAMS_SIZE,
2948dab45b6Stkusumi "corrupt_bio_byte %u %c %u %u ",
2958dab45b6Stkusumi tfc->corrupt_buf_byte,
2968dab45b6Stkusumi FLAKEY_CORRUPT_DIR(tfc),
2978dab45b6Stkusumi tfc->corrupt_buf_value,
2988dab45b6Stkusumi tfc->corrupt_buf_flags);
2998dab45b6Stkusumi *(--p) = '\0';
3008dab45b6Stkusumi
3018dab45b6Stkusumi return params;
3028dab45b6Stkusumi }
3038dab45b6Stkusumi
3048dab45b6Stkusumi #ifdef DEBUG_FLAKEY
3058dab45b6Stkusumi static int count = 0;
3068dab45b6Stkusumi #endif
3078dab45b6Stkusumi
3088dab45b6Stkusumi int
dm_target_flakey_strategy(dm_table_entry_t * table_en,struct buf * bp)3098dab45b6Stkusumi dm_target_flakey_strategy(dm_table_entry_t *table_en, struct buf *bp)
3108dab45b6Stkusumi {
3118dab45b6Stkusumi dm_target_flakey_config_t *tfc;
3128dab45b6Stkusumi #ifndef DEBUG_FLAKEY
3138dab45b6Stkusumi int elapsed;
3148dab45b6Stkusumi #endif
3158dab45b6Stkusumi
3168dab45b6Stkusumi tfc = table_en->target_config;
3178dab45b6Stkusumi #ifndef DEBUG_FLAKEY
3188dab45b6Stkusumi elapsed = (tick - tfc->offset_time) / hz;
3198dab45b6Stkusumi if (elapsed % (tfc->up_int + tfc->down_int) >= tfc->up_int) {
3208dab45b6Stkusumi #else
3218dab45b6Stkusumi if (++count % 100 == 0) {
3228dab45b6Stkusumi #endif
3238dab45b6Stkusumi if (bp->b_flags & B_READ)
3248dab45b6Stkusumi return _flakey_read(tfc, bp);
3258dab45b6Stkusumi else
3268dab45b6Stkusumi return _flakey_write(tfc, bp);
3278dab45b6Stkusumi }
3288dab45b6Stkusumi
3298dab45b6Stkusumi /* This is what linear target does */
3308dab45b6Stkusumi _submit(tfc, bp);
3318dab45b6Stkusumi
3328dab45b6Stkusumi return 0;
3338dab45b6Stkusumi }
3348dab45b6Stkusumi
3358dab45b6Stkusumi static __inline void
3368dab45b6Stkusumi _submit(dm_target_flakey_config_t *tfc, struct buf *bp)
3378dab45b6Stkusumi {
3388dab45b6Stkusumi
3398dab45b6Stkusumi bp->b_blkno += tfc->offset;
3408dab45b6Stkusumi VOP_STRATEGY(tfc->pdev->pdev_vnode, bp);
3418dab45b6Stkusumi }
3428dab45b6Stkusumi
3438dab45b6Stkusumi static __inline void
3448dab45b6Stkusumi _flakey_eio_buf(struct buf *bp)
3458dab45b6Stkusumi {
3468dab45b6Stkusumi
3478dab45b6Stkusumi bp->b_error = EIO;
3488dab45b6Stkusumi bp->b_resid = 0;
3498dab45b6Stkusumi }
3508dab45b6Stkusumi
3518dab45b6Stkusumi static void
3528dab45b6Stkusumi _flakey_nestiobuf_iodone(buf_t *bp)
3538dab45b6Stkusumi {
3548dab45b6Stkusumi #ifdef HAS_BUF_PRIV2
3558dab45b6Stkusumi dm_target_flakey_config_t *tfc;
3568dab45b6Stkusumi #endif
3578dab45b6Stkusumi buf_t *mbp = bp->b_private;
3588dab45b6Stkusumi int error;
3598dab45b6Stkusumi int donebytes;
3608dab45b6Stkusumi
3618dab45b6Stkusumi KASSERT(bp->b_bcount <= bp->b_bufsize);
3628dab45b6Stkusumi KASSERT(mbp != bp);
3638dab45b6Stkusumi
3648dab45b6Stkusumi error = bp->b_error;
3658dab45b6Stkusumi if (bp->b_error == 0 &&
3668dab45b6Stkusumi (bp->b_bcount < bp->b_bufsize || bp->b_resid > 0)) {
3678dab45b6Stkusumi /*
368*7991f5a7Sandvar * Not all got transferred, raise an error. We have no way to
3698dab45b6Stkusumi * propagate these conditions to mbp.
3708dab45b6Stkusumi */
3718dab45b6Stkusumi error = EIO;
3728dab45b6Stkusumi }
3738dab45b6Stkusumi
3748dab45b6Stkusumi #ifdef HAS_BUF_PRIV2
3758dab45b6Stkusumi tfc = bp->b_private2;
3768dab45b6Stkusumi /*
3778dab45b6Stkusumi * Linux dm-flakey has changed its read behavior in 2016.
3788dab45b6Stkusumi * This conditional is to sync with that change.
3798dab45b6Stkusumi */
3808dab45b6Stkusumi if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_READ)
3818dab45b6Stkusumi _flakey_corrupt_buf(tfc, mbp);
3828dab45b6Stkusumi else if (!tfc->drop_writes)
3838dab45b6Stkusumi _flakey_eio_buf(mbp);
3848dab45b6Stkusumi #endif
3858dab45b6Stkusumi donebytes = bp->b_bufsize;
3868dab45b6Stkusumi putiobuf(bp);
3878dab45b6Stkusumi nestiobuf_done(mbp, donebytes, error);
3888dab45b6Stkusumi }
3898dab45b6Stkusumi
3908dab45b6Stkusumi static int
3918dab45b6Stkusumi _flakey_read(dm_target_flakey_config_t *tfc, struct buf *bp)
3928dab45b6Stkusumi {
3938dab45b6Stkusumi struct buf *nestbuf;
3948dab45b6Stkusumi
3958dab45b6Stkusumi /*
3968dab45b6Stkusumi * Linux dm-flakey has changed its read behavior in 2016.
3978dab45b6Stkusumi * This conditional is to sync with that change.
3988dab45b6Stkusumi */
3998dab45b6Stkusumi if (!tfc->corrupt_buf_byte && !tfc->drop_writes) {
4008dab45b6Stkusumi _flakey_eio_buf(bp);
4018dab45b6Stkusumi biodone(bp);
4028dab45b6Stkusumi return 0;
4038dab45b6Stkusumi }
4048dab45b6Stkusumi
4058dab45b6Stkusumi nestbuf = getiobuf(NULL, true);
4068dab45b6Stkusumi nestiobuf_setup(bp, nestbuf, 0, bp->b_bcount);
4078dab45b6Stkusumi nestbuf->b_iodone = _flakey_nestiobuf_iodone;
4088dab45b6Stkusumi nestbuf->b_blkno = bp->b_blkno;
4098dab45b6Stkusumi #ifdef HAS_BUF_PRIV2
4108dab45b6Stkusumi nestbuf->b_private2 = tfc;
4118dab45b6Stkusumi #endif
4128dab45b6Stkusumi _submit(tfc, nestbuf);
4138dab45b6Stkusumi
4148dab45b6Stkusumi return 0;
4158dab45b6Stkusumi }
4168dab45b6Stkusumi
4178dab45b6Stkusumi static int
4188dab45b6Stkusumi _flakey_write(dm_target_flakey_config_t *tfc, struct buf *bp)
4198dab45b6Stkusumi {
4208dab45b6Stkusumi
4218dab45b6Stkusumi if (tfc->drop_writes) {
4228dab45b6Stkusumi aprint_debug("bp=%p drop_writes blkno=%ju\n", bp, bp->b_blkno);
4238dab45b6Stkusumi biodone(bp);
4248dab45b6Stkusumi return 0;
4258dab45b6Stkusumi }
4268dab45b6Stkusumi
4278dab45b6Stkusumi if (tfc->corrupt_buf_byte && tfc->corrupt_buf_rw == BUF_CMD_WRITE) {
4288dab45b6Stkusumi _flakey_corrupt_buf(tfc, bp);
4298dab45b6Stkusumi _submit(tfc, bp);
4308dab45b6Stkusumi return 0;
4318dab45b6Stkusumi }
4328dab45b6Stkusumi
4338dab45b6Stkusumi /* Error all I/Os if neither of the above two */
4348dab45b6Stkusumi _flakey_eio_buf(bp);
4358dab45b6Stkusumi biodone(bp);
4368dab45b6Stkusumi
4378dab45b6Stkusumi return 0;
4388dab45b6Stkusumi }
4398dab45b6Stkusumi
4408dab45b6Stkusumi static int
4418dab45b6Stkusumi _flakey_corrupt_buf(dm_target_flakey_config_t *tfc, struct buf *bp)
4428dab45b6Stkusumi {
4438dab45b6Stkusumi char *buf;
4448dab45b6Stkusumi
4458dab45b6Stkusumi if (bp->b_data == NULL)
4468dab45b6Stkusumi return 1;
4478dab45b6Stkusumi if (bp->b_error)
4488dab45b6Stkusumi return 1; /* Don't corrupt on error */
4498dab45b6Stkusumi if (bp->b_bcount < tfc->corrupt_buf_byte)
4508dab45b6Stkusumi return 1;
4518dab45b6Stkusumi if ((bp->b_flags & tfc->corrupt_buf_flags) != tfc->corrupt_buf_flags)
4528dab45b6Stkusumi return 1;
4538dab45b6Stkusumi
4548dab45b6Stkusumi buf = bp->b_data;
4558dab45b6Stkusumi buf[tfc->corrupt_buf_byte - 1] = tfc->corrupt_buf_value;
4568dab45b6Stkusumi
4578dab45b6Stkusumi aprint_debug("bp=%p dir=%c blkno=%ju Nth=%u value=%u\n",
4588dab45b6Stkusumi bp, FLAKEY_CORRUPT_DIR(tfc), bp->b_blkno, tfc->corrupt_buf_byte,
4598dab45b6Stkusumi tfc->corrupt_buf_value);
4608dab45b6Stkusumi
4618dab45b6Stkusumi return 0;
4628dab45b6Stkusumi }
4638dab45b6Stkusumi
4648dab45b6Stkusumi int
4658dab45b6Stkusumi dm_target_flakey_sync(dm_table_entry_t *table_en)
4668dab45b6Stkusumi {
4678dab45b6Stkusumi dm_target_flakey_config_t *tfc;
4688dab45b6Stkusumi int cmd;
4698dab45b6Stkusumi
4708dab45b6Stkusumi tfc = table_en->target_config;
4718dab45b6Stkusumi cmd = 1;
4728dab45b6Stkusumi
4738dab45b6Stkusumi return VOP_IOCTL(tfc->pdev->pdev_vnode, DIOCCACHESYNC, &cmd,
4748dab45b6Stkusumi FREAD | FWRITE, kauth_cred_get());
4758dab45b6Stkusumi }
4768dab45b6Stkusumi
4778dab45b6Stkusumi int
4788dab45b6Stkusumi dm_target_flakey_destroy(dm_table_entry_t *table_en)
4798dab45b6Stkusumi {
4808dab45b6Stkusumi
4818dab45b6Stkusumi if (table_en->target_config == NULL)
4828dab45b6Stkusumi goto out;
4838dab45b6Stkusumi
4848dab45b6Stkusumi dm_target_flakey_config_t *tfc = table_en->target_config;
4858dab45b6Stkusumi
4868dab45b6Stkusumi /* Decrement pdev ref counter if 0 remove it */
4878dab45b6Stkusumi dm_pdev_decr(tfc->pdev);
4888dab45b6Stkusumi
4898dab45b6Stkusumi kmem_free(tfc, sizeof(*tfc));
4908dab45b6Stkusumi out:
4918dab45b6Stkusumi /* Unbusy target so we can unload it */
4928dab45b6Stkusumi dm_target_unbusy(table_en->target);
4938dab45b6Stkusumi
4948dab45b6Stkusumi return 0;
4958dab45b6Stkusumi }
4968dab45b6Stkusumi
49767de4a93Stkusumi #if 0
4988dab45b6Stkusumi int
4998dab45b6Stkusumi dm_target_flakey_upcall(dm_table_entry_t *table_en, struct buf *bp)
5008dab45b6Stkusumi {
5018dab45b6Stkusumi
5028dab45b6Stkusumi return 0;
5038dab45b6Stkusumi }
50467de4a93Stkusumi #endif
5058dab45b6Stkusumi
5068dab45b6Stkusumi int
5078dab45b6Stkusumi dm_target_flakey_secsize(dm_table_entry_t *table_en, unsigned int *secsizep)
5088dab45b6Stkusumi {
5098dab45b6Stkusumi dm_target_flakey_config_t *tfc;
5108dab45b6Stkusumi unsigned int secsize;
5118dab45b6Stkusumi
5128dab45b6Stkusumi secsize = 0;
5138dab45b6Stkusumi
5148dab45b6Stkusumi tfc = table_en->target_config;
5158dab45b6Stkusumi if (tfc != NULL)
5168dab45b6Stkusumi secsize = tfc->pdev->pdev_secsize;
5178dab45b6Stkusumi
5188dab45b6Stkusumi *secsizep = secsize;
5198dab45b6Stkusumi
5208dab45b6Stkusumi return 0;
5218dab45b6Stkusumi }
522