There isn’t an existing command – you’ll need to issue the DKIOCFREE ioctl..

I’ve attached a program that’ll do it – I wrote this while testing some of the 
TRIM support (but obviously use at your own risk). While it was originally used 
w/ zvols (hence the name), it should work for any block device that supports 
the DKIOCFREE ioctl.

Either call it with -a /dev/rdsk/… to do the whole disk, or you can give it a 
list of LBA + lengths to TRIM specific portions of a disk..


From: Marcel Telka <[email protected]>
Date: Wednesday, August 4, 2021 at 8:49 AM
To: [email protected] <[email protected]>
Subject: [discuss] Disk TRIM without zfs?
Hi,

Is there a way to TRIM a disk without creating a zfs pool on it?


Thanks.

--
+-------------------------------------------+
| Marcel Telka   e-mail:   [email protected]  |
|                homepage: http://telka.sk/ |
+-------------------------------------------+

------------------------------------------
illumos: illumos-discuss
Permalink: 
https://illumos.topicbox.com/groups/discuss/Tb82006464e3b5173-Mfc3d2ddcba605a7a052fca73
Delivery options: https://illumos.topicbox.com/groups/discuss/subscription
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2020 Joyent, Inc.
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/dkio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <err.h>
#include <unistd.h>

static uint64_t
get_size(const char *dev, int fd)
{
        struct dk_minfo dkm = { 0 };
        int bit = 0;

        if (ioctl(fd, DKIOCGMEDIAINFO, &dkm) < 0)
                err(EXIT_FAILURE, "ioctl(%s, DKIOCGMEDIAINFO) failed", dev);

        if (dkm.dki_lbsize > 0)
                bit = ffs((int)dkm.dki_lbsize) - 1;

        return ((uint64_t)dkm.dki_capacity << (uint_t)bit);
}

static uint64_t
get_num(const char *str)
{
        uint64_t val;

        errno = 0;
        val = strtoull(str, NULL, 0);
        if (errno != 0)
                err(EXIT_FAILURE, "could not parse '%s'", str);
        return (val);
}

static void
usage(void)
{
        fprintf(stderr,
            "Usage: %s [-aw] dev\n"
            "       %s [-w] dev [offset length] [offset length...]\n",
            getprogname(), getprogname());
        exit(2);
}

static dkioc_free_list_t *
make_list(int argc, char **argv)
{
        dkioc_free_list_t *dfl = NULL;

        if (argc % 2 == 1)
                errx(EXIT_FAILURE, "odd number of parameters given");

        if ((dfl = calloc(1, DFL_SZ(argc / 2))) == NULL)
                err(EXIT_FAILURE, "no memory");

        dfl->dfl_num_exts = argc / 2;
        for (size_t i = 0; i < argc / 2; i++) {
                dfl->dfl_exts[i].dfle_start = get_num(argv[i * 2]);
                dfl->dfl_exts[i].dfle_length = get_num(argv[(i * 2) + 1]);
        }

        return (dfl);
}

static dkioc_free_list_t *
make_all(const char *dev, int fd)
{
        dkioc_free_list_t *dfl = NULL;
        uint64_t len = get_size(dev, fd);

        if ((dfl = calloc(1, DFL_SZ(1))) == NULL)
                err(EXIT_FAILURE, "no memory");

        dfl->dfl_num_exts = 1;
        dfl->dfl_exts[0].dfle_start = 0;
        dfl->dfl_exts[0].dfle_length = len;

        return (dfl);
}


/*
 * This issues a DKIOCFREE ioctl for a block device for the entire
 * size of the device. Use with caution!.
 */
int
main(int argc, char **argv)
{
        const char *devname = NULL;
        dkioc_free_list_t *dfl;
        boolean_t all = B_FALSE;
        boolean_t wait = B_FALSE;
        int fd = -1;
        int c;

        while ((c = getopt(argc, argv, "aw")) != -1) {
                switch (c) {
                case 'a':
                        all = B_TRUE;
                        break;
                case 'w':
                        wait = B_TRUE;
                        break;
                case '?':
                        fprintf(stderr, "Invalid option -%c\n", optopt);
                        usage();
                }
        }

        if (argc == optind)
                usage();

        devname = argv[optind];
        argc -= optind + 1;
        argv += optind + 1;

        if ((fd = open(devname, O_RDWR)) < 0)
                err(EXIT_FAILURE, "open %s failed", devname);

        dfl = all ? make_all(devname, fd) : make_list(argc, argv);

        printf("%s:\n", devname);
        for (size_t i = 0; i < dfl->dfl_num_exts; i++) {
                printf("    [%zu] start = %llu length = %llu\n", i,
                    dfl->dfl_exts[i].dfle_start,
                    dfl->dfl_exts[i].dfle_length);
        }

        if (wait)
                dfl->dfl_flags |= DF_WAIT_SYNC;

        if (ioctl(fd, DKIOCFREE, dfl) < 0)
                err(EXIT_FAILURE, "ioctl(%s, DKIOCFREE) failed", devname);

        (void) close(fd);
        return (0);
}

Reply via email to