diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2017-09-05 11:33:35 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2017-09-05 11:33:35 +0000 |
commit | 9105a020d63cb56f2be32de07d869c429cf3d4a8 (patch) | |
tree | 15099569f7776b11f1f5dcb909fd2722b68dd68a | |
parent | ae3f6fefe9c3bb258f65cb0b40e132eeef10fb5a (diff) | |
parent | 27b4c18acb5aee865d218de91c475ae0a7cc4e8a (diff) | |
download | illumos-joyent-9105a020d63cb56f2be32de07d869c429cf3d4a8.tar.gz |
[illumos-gate merge]
commit 27b4c18acb5aee865d218de91c475ae0a7cc4e8a
8598 loader: Fix BSD label partition end sector calculation.
commit 0a0551200ecbcd4f1b17560acaeeb7b6c8b0090e
640 number_to_scaled_string is duplicated in several commands
commit 30c75cb09b4d5e86a94a25a9a7ab7481510b57b0
8093 loader.efi: cleanup loader main and implement comconsole
commit ce1d04bfb4326d1e894811fc63d56219b33f2f7a
8447 uts: acpica_check_bios_date() instead of bios-free, use efi-systab
commit a62d310a2be18bb1054fea448f82a292724aa661
8445 uts: pci_bios_get_irq_routing() should check for BIOS
commit 1271e4b10dfaaed576c08a812f466f6e81370e5e
8585 improve batching done in zil_commit()
commit e6ab4525d156c82445c116ecf6b2b874d5e9009d
8590 memory leak in dsl_destroy_snapshots_nvl()
49 files changed, 2511 insertions, 1121 deletions
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version index 4a0d16a0d4..a90435f0cd 100644 --- a/usr/src/boot/Makefile.version +++ b/usr/src/boot/Makefile.version @@ -33,4 +33,4 @@ LOADER_VERSION = 1.1 # Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes. # The version is processed from left to right, the version number can only # be increased. -BOOT_VERSION = $(LOADER_VERSION)-2017.6.29.1 +BOOT_VERSION = $(LOADER_VERSION)-2017.8.31.1 diff --git a/usr/src/boot/sys/boot/common/bootstrap.h b/usr/src/boot/sys/boot/common/bootstrap.h index 83495af92c..f963ad5391 100644 --- a/usr/src/boot/sys/boot/common/bootstrap.h +++ b/usr/src/boot/sys/boot/common/bootstrap.h @@ -108,7 +108,7 @@ struct console void (*c_out)(struct console *, int); /* emit c */ int (*c_in)(struct console *); /* wait for and return input */ int (*c_ready)(struct console *); /* return nonzer if input waiting */ - void *private; /* private data */ + void *c_private; /* private data */ }; extern struct console *consoles[]; void cons_probe(void); diff --git a/usr/src/boot/sys/boot/common/part.c b/usr/src/boot/sys/boot/common/part.c index 8366c88234..0a91aa768e 100644 --- a/usr/src/boot/sys/boot/common/part.c +++ b/usr/src/boot/sys/boot/common/part.c @@ -530,7 +530,7 @@ ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) break; entry->part.start = le32toh(part->p_offset) - raw_offset; entry->part.end = entry->part.start + - le32toh(part->p_size) + 1; + le32toh(part->p_size) - 1; entry->part.type = bsd_parttype(part->p_fstype); entry->part.index = i; /* starts from zero */ entry->type.bsd = part->p_fstype; diff --git a/usr/src/boot/sys/boot/efi/loader/comconsole.c b/usr/src/boot/sys/boot/efi/loader/comconsole.c new file mode 100644 index 0000000000..539637b833 --- /dev/null +++ b/usr/src/boot/sys/boot/efi/loader/comconsole.c @@ -0,0 +1,551 @@ +/* + * Copyright (c) 1998 Michael Smith (msmith@freebsd.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#include <stand.h> +#include <sys/errno.h> +#include <bootstrap.h> + +#include <efi.h> +#include <efilib.h> + +#include "loader_efi.h" + +static EFI_GUID serial = SERIAL_IO_PROTOCOL; + +#define COMC_TXWAIT 0x40000 /* transmit timeout */ + +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +struct serial { + uint64_t baudrate; + uint8_t databits; + EFI_PARITY_TYPE parity; + EFI_STOP_BITS_TYPE stopbits; + uint8_t ignore_cd; /* boolean */ + uint8_t rtsdtr_off; /* boolean */ + int ioaddr; /* index in handles array */ + SERIAL_IO_INTERFACE *sio; +}; + +static void comc_probe(struct console *); +static int comc_init(struct console *, int); +static void comc_putchar(struct console *, int); +static int comc_getchar(struct console *); +static int comc_ischar(struct console *); +static void comc_setup(struct console *); +static char *comc_asprint_mode(struct serial *); +static int comc_parse_mode(struct serial *, const char *); +static int comc_mode_set(struct env_var *, int, const void *); +static int comc_cd_set(struct env_var *, int, const void *); +static int comc_rtsdtr_set(struct env_var *, int, const void *); + +struct console ttya = { + .c_name = "ttya", + .c_desc = "serial port a", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL +}; + +struct console ttyb = { + .c_name = "ttyb", + .c_desc = "serial port b", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL +}; + +struct console ttyc = { + .c_name = "ttyc", + .c_desc = "serial port c", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL +}; + +struct console ttyd = { + .c_name = "ttyd", + .c_desc = "serial port d", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL +}; + +EFI_STATUS +efi_serial_init(EFI_HANDLE **handlep, int *nhandles) +{ + UINTN bufsz = 0; + EFI_STATUS status; + EFI_HANDLE *handles; + + /* + * get buffer size + */ + *nhandles = 0; + status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); + if (status != EFI_BUFFER_TOO_SMALL) + return (status); + + if ((handles = malloc(bufsz)) == NULL) + return (ENOMEM); + + *nhandles = (int)(bufsz/sizeof (EFI_HANDLE)); + /* + * get handle array + */ + status = BS->LocateHandle(ByProtocol, &serial, NULL, &bufsz, handles); + if (EFI_ERROR(status)) { + free(handles); + *nhandles = 0; + } else + *handlep = handles; + return (status); +} + +static void +comc_probe(struct console *cp) +{ + EFI_STATUS status; + struct serial *port; + char name[20]; + char value[20]; + char *cons, *env; + EFI_HANDLE *handles = NULL; /* array of handles */ + int nhandles = 0; /* number of handles in array */ + + /* are we already set up? */ + if (cp->c_private != NULL) + return; + + /* make sure the handles are available */ + status = efi_serial_init(&handles, &nhandles); + + cp->c_private = malloc(sizeof (struct serial)); + port = cp->c_private; + port->baudrate = COMSPEED; + + if (strcmp(cp->c_name, "ttya") == 0) + port->ioaddr = 0; + else if (strcmp(cp->c_name, "ttyb") == 0) + port->ioaddr = 1; + else if (strcmp(cp->c_name, "ttyc") == 0) + port->ioaddr = 2; + else if (strcmp(cp->c_name, "ttyd") == 0) + port->ioaddr = 3; + + if (port->ioaddr >= nhandles) + port->ioaddr = -1; /* invalid port */ + + port->databits = 8; /* 8,n,1 */ + port->parity = NoParity; /* 8,n,1 */ + port->stopbits = OneStopBit; /* 8,n,1 */ + port->ignore_cd = 1; /* ignore cd */ + port->rtsdtr_off = 0; /* rts-dtr is on */ + port->sio = NULL; + + if (port->ioaddr != -1) { + status = BS->OpenProtocol(handles[port->ioaddr], + &serial, (void**)&port->sio, IH, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (EFI_ERROR(status)) + port->ioaddr = -1; /* invalid port */ + } + if (handles != NULL) + free(handles); + + snprintf(name, sizeof (name), "%s-mode", cp->c_name); + env = getenv(name); + + if (env != NULL) + (void) comc_parse_mode(port, env); + + env = comc_asprint_mode(port); + + if (env != NULL) { + unsetenv(name); + env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); + free(env); + } + + snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); + env = getenv(name); + if (env != NULL) { + if (strcmp(env, "true") == 0) + port->ignore_cd = 1; + else if (strcmp(env, "false") == 0) + port->ignore_cd = 0; + } + + snprintf(value, sizeof (value), "%s", + port->ignore_cd? "true" : "false"); + unsetenv(name); + env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); + + snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); + env = getenv(name); + if (env != NULL) { + if (strcmp(env, "true") == 0) + port->rtsdtr_off = 1; + else if (strcmp(env, "false") == 0) + port->rtsdtr_off = 0; + } + + snprintf(value, sizeof (value), "%s", + port->rtsdtr_off? "true" : "false"); + unsetenv(name); + env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); + comc_setup(cp); +} + +static int +comc_init(struct console *cp, int arg __attribute((unused))) +{ + + comc_setup(cp); + + if ((cp->c_flags & (C_PRESENTIN | C_PRESENTOUT)) == + (C_PRESENTIN | C_PRESENTOUT)) + return (CMD_OK); + return (CMD_ERROR); +} + +static void +comc_putchar(struct console *cp, int c) +{ + int wait; + EFI_STATUS status; + UINTN bufsz = 1; + char cb = c; + struct serial *sp = cp->c_private; + + if (sp->sio == NULL) + return; + + for (wait = COMC_TXWAIT; wait > 0; wait--) { + status = sp->sio->Write(sp->sio, &bufsz, &cb); + if (status != EFI_TIMEOUT) + break; + } +} + +static int +comc_getchar(struct console *cp) +{ + EFI_STATUS status; + UINTN bufsz = 1; + char c; + struct serial *sp = cp->c_private; + + if (sp->sio == NULL || !comc_ischar(cp)) + return (-1); + + status = sp->sio->Read(sp->sio, &bufsz, &c); + if (EFI_ERROR(status) || bufsz == 0) + return (-1); + + return (c); +} + +static int +comc_ischar(struct console *cp) +{ + EFI_STATUS status; + uint32_t control; + struct serial *sp = cp->c_private; + + if (sp->sio == NULL) + return (0); + + status = sp->sio->GetControl(sp->sio, &control); + if (EFI_ERROR(status)) + return (0); + + return (!(status & EFI_SERIAL_INPUT_BUFFER_EMPTY)); +} + +static char * +comc_asprint_mode(struct serial *sp) +{ + char par = 'n', *buf; + int stop = 1; + + if (sp == NULL) + return (NULL); + + switch (sp->parity) { + case NoParity: par = 'n'; + break; + case EvenParity: par = 'e'; + break; + case OddParity: par = 'o'; + break; + } + switch (sp->stopbits) { + case OneStopBit: stop = 1; + break; + case TwoStopBits: stop = 2; + break; + } + + asprintf(&buf, "%ju,%d,%c,%d,-", sp->baudrate, sp->databits, par, stop); + return (buf); +} + +static int +comc_parse_mode(struct serial *sp, const char *value) +{ + unsigned long n; + uint64_t baudrate; + uint8_t databits = 8; + int parity = NoParity; + int stopbits = OneStopBit; + char *ep; + + if (value == NULL || *value == '\0') + return (CMD_ERROR); + + errno = 0; + n = strtoul(value, &ep, 10); + if (errno != 0 || *ep != ',') + return (CMD_ERROR); + baudrate = n; + + ep++; + n = strtoul(ep, &ep, 10); + if (errno != 0 || *ep != ',') + return (CMD_ERROR); + + switch (n) { + case 7: databits = 7; + break; + case 8: databits = 8; + break; + default: + return (CMD_ERROR); + } + + ep++; + switch (*ep++) { + case 'n': parity = NoParity; + break; + case 'e': parity = EvenParity; + break; + case 'o': parity = OddParity; + break; + default: + return (CMD_ERROR); + } + + if (*ep == ',') + ep++; + else + return (CMD_ERROR); + + switch (*ep++) { + case '1': stopbits = OneStopBit; + break; + case '2': stopbits = TwoStopBits; + break; + default: + return (CMD_ERROR); + } + + /* handshake is ignored, but we check syntax anyhow */ + if (*ep == ',') + ep++; + else + return (CMD_ERROR); + + switch (*ep++) { + case '-': + case 'h': + case 's': + break; + default: + return (CMD_ERROR); + } + + if (*ep != '\0') + return (CMD_ERROR); + + sp->baudrate = baudrate; + sp->databits = databits; + sp->parity = parity; + sp->stopbits = stopbits; + return (CMD_OK); +} + +static struct console * +get_console(char *name) +{ + struct console *cp = NULL; + + switch (name[3]) { + case 'a': cp = &ttya; + break; + case 'b': cp = &ttyb; + break; + case 'c': cp = &ttyc; + break; + case 'd': cp = &ttyd; + break; + } + return (cp); +} + +static int +comc_mode_set(struct env_var *ev, int flags, const void *value) +{ + struct console *cp; + + if (value == NULL) + return (CMD_ERROR); + + if ((cp = get_console(ev->ev_name)) == NULL) + return (CMD_ERROR); + + if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) + return (CMD_ERROR); + + comc_setup(cp); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +static int +comc_cd_set(struct env_var *ev, int flags, const void *value) +{ + struct console *cp; + struct serial *sp; + + if (value == NULL) + return (CMD_ERROR); + + if ((cp = get_console(ev->ev_name)) == NULL) + return (CMD_ERROR); + + sp = cp->c_private; + if (strcmp(value, "true") == 0) + sp->ignore_cd = 1; + else if (strcmp(value, "false") == 0) + sp->ignore_cd = 0; + else + return (CMD_ERROR); + + comc_setup(cp); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +static int +comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) +{ + struct console *cp; + struct serial *sp; + + if (value == NULL) + return (CMD_ERROR); + + if ((cp = get_console(ev->ev_name)) == NULL) + return (CMD_ERROR); + + sp = cp->c_private; + if (strcmp(value, "true") == 0) + sp->rtsdtr_off = 1; + else if (strcmp(value, "false") == 0) + sp->rtsdtr_off = 0; + else + return (CMD_ERROR); + + comc_setup(cp); + env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); + + return (CMD_OK); +} + +static void +comc_setup(struct console *cp) +{ + EFI_STATUS status; + UINT32 control; + struct serial *sp = cp->c_private; + + if ((cp->c_flags & (C_ACTIVEIN | C_ACTIVEOUT)) == 0) + return; + + /* port is not usable */ + if (sp->sio == NULL) { + cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); + return; + } + + cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT); + status = sp->sio->Reset(sp->sio); + if (EFI_ERROR(status)) { + cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); + } + + status = sp->sio->SetAttributes(sp->sio, sp->baudrate, 0, 0, sp->parity, + sp->databits, sp->stopbits); + if (EFI_ERROR(status)) { + cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); + } + + if (sp->rtsdtr_off) { + status = sp->sio->GetControl(sp->sio, &control); + if (EFI_ERROR(status)) { + cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); + } + control &= ~(EFI_SERIAL_REQUEST_TO_SEND | + EFI_SERIAL_DATA_TERMINAL_READY); + status = sp->sio->SetControl(sp->sio, control); + if (EFI_ERROR(status)) { + cp->c_flags &= ~(C_PRESENTIN | C_PRESENTOUT); + } + } +} diff --git a/usr/src/boot/sys/boot/efi/loader/main.c b/usr/src/boot/sys/boot/efi/loader/main.c index 472ce86509..3ef635fd91 100644 --- a/usr/src/boot/sys/boot/efi/loader/main.c +++ b/usr/src/boot/sys/boot/efi/loader/main.c @@ -41,7 +41,6 @@ #include <efigpt.h> #include <uuid.h> -#include <stdbool.h> #include <bootstrap.h> #include <smbios.h> @@ -61,10 +60,8 @@ EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; -EFI_GUID serial_io = SERIAL_IO_PROTOCOL; extern void acpi_detect(void); -void efi_serial_init(void); extern void efi_getsmap(void); #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); @@ -415,7 +412,6 @@ main(int argc, CHAR16 *argv[]) ptr = efi_get_table(&smbios); smbios_detect(ptr); - efi_serial_init(); /* detect and set up serial ports */ interact(NULL); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ @@ -424,8 +420,7 @@ main(int argc, CHAR16 *argv[]) COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int -command_reboot(int argc __attribute((unused)), - char *argv[] __attribute((unused))) +command_reboot(int argc __unused, char *argv[] __unused) { int i; @@ -442,8 +437,7 @@ command_reboot(int argc __attribute((unused)), COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int -command_memmap(int argc __attribute((unused)), - char *argv[] __attribute((unused))) +command_memmap(int argc __unused, char *argv[] __unused) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; @@ -523,8 +517,7 @@ COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static int -command_configuration(int argc __attribute((unused)), - char *argv[] __attribute((unused))) +command_configuration(int argc __unused, char *argv[] __unused) { UINTN i; char *name; @@ -608,8 +601,7 @@ command_mode(int argc, char *argv[]) COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi); static int -command_lsefi(int argc __attribute((unused)), - char *argv[] __attribute((unused))) +command_lsefi(int argc __unused, char *argv[] __unused) { char *name; EFI_HANDLE *buffer = NULL; @@ -730,53 +722,6 @@ command_reloadbe(int argc, char *argv[]) } #endif /* __FreeBSD__ */ -void -efi_serial_init(void) -{ - EFI_HANDLE *buffer = NULL; - UINTN bufsz = 0, i; - EFI_STATUS status; - int serial = 0; - - /* - * get buffer size - */ - status = BS->LocateHandle(ByProtocol, &serial_io, NULL, &bufsz, buffer); - if (status != EFI_BUFFER_TOO_SMALL) { - snprintf(command_errbuf, sizeof (command_errbuf), - "unexpected error: %lld", (long long)status); - return; - } - if ((buffer = malloc(bufsz)) == NULL) { - sprintf(command_errbuf, "out of memory"); - return; - } - - /* - * get handle array - */ - status = BS->LocateHandle(ByProtocol, &serial_io, NULL, &bufsz, buffer); - if (EFI_ERROR(status)) { - free(buffer); - snprintf(command_errbuf, sizeof (command_errbuf), - "LocateHandle() error: %lld", (long long)status); - return; - } - - for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) { - SERIAL_IO_INTERFACE *sio; - status = BS->OpenProtocol(buffer[i], &serial_io, (void**)&sio, - IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(status)) { - snprintf(command_errbuf, sizeof (command_errbuf), - "OpenProtocol() error: %lld", (long long)status); - } - printf("serial# %d\n", serial++); - } - - free(buffer); -} - #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); diff --git a/usr/src/boot/sys/boot/i386/libfirewire/dconsole.c b/usr/src/boot/sys/boot/i386/libfirewire/dconsole.c index eccdc77799..a77e4ba098 100644 --- a/usr/src/boot/sys/boot/i386/libfirewire/dconsole.c +++ b/usr/src/boot/sys/boot/i386/libfirewire/dconsole.c @@ -60,14 +60,15 @@ uint32_t dcons_paddr; static char dcons_buffer[DCONS_BUF_SIZE + PAGE_SIZE]; struct console dconsole = { - "dcons", - "dumb console port", - 0, - dconsole_probe, - dconsole_init, - dconsole_putchar, - dconsole_getchar, - dconsole_ischar + .c_name = "dcons", + .c_desc = "dumb console port", + .c_flags = 0, + .c_probe = dconsole_probe, + .c_init = dconsole_init, + .c_out = dconsole_putchar, + .c_in = dconsole_getchar, + .c_ready = dconsole_ischar, + .c_private = NULL }; #define DCONSOLE_AS_MULTI_CONSOLE 1 diff --git a/usr/src/boot/sys/boot/i386/libi386/comconsole.c b/usr/src/boot/sys/boot/i386/libi386/comconsole.c index a8ce7906a0..6f24526ac8 100644 --- a/usr/src/boot/sys/boot/i386/libi386/comconsole.c +++ b/usr/src/boot/sys/boot/i386/libi386/comconsole.c @@ -67,69 +67,68 @@ struct serial { uint32_t locator; }; -static void comc_probe(struct console *cp); -static int comc_init(struct console *cp, int arg); -static void comc_putchar(struct console *cp, int c); -static int comc_getchar(struct console *cp); -static int comc_getspeed(struct serial *sp); -static int comc_ischar(struct console *cp); -static uint32_t comc_parse_pcidev(const char *string); -static int comc_pcidev_set(struct env_var *ev, int flags, - const void *value); -static int comc_pcidev_handle(struct console *cp, uint32_t locator); -static void comc_setup(struct console *cp); -static char *comc_print_mode(struct serial *sp, char *buf); -static int comc_parse_mode(struct serial *sp, const char *value); +static void comc_probe(struct console *); +static int comc_init(struct console *, int); +static void comc_putchar(struct console *, int); +static int comc_getchar(struct console *); +static int comc_getspeed(struct serial *); +static int comc_ischar(struct console *); +static uint32_t comc_parse_pcidev(const char *); +static int comc_pcidev_set(struct env_var *, int, const void *); +static int comc_pcidev_handle(struct console *, uint32_t); +static void comc_setup(struct console *); +static char *comc_asprint_mode(struct serial *); +static int comc_parse_mode(struct serial *, const char *); static int comc_mode_set(struct env_var *, int, const void *); static int comc_cd_set(struct env_var *, int, const void *); static int comc_rtsdtr_set(struct env_var *, int, const void *); struct console ttya = { - "ttya", - "serial port a", - 0, - comc_probe, - comc_init, - comc_putchar, - comc_getchar, - comc_ischar, - NULL + .c_name = "ttya", + .c_desc = "serial port a", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL }; struct console ttyb = { - "ttyb", - "serial port b", - 0, - comc_probe, - comc_init, - comc_putchar, - comc_getchar, - comc_ischar, - NULL + .c_name = "ttyb", + .c_desc = "serial port b", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL }; struct console ttyc = { - "ttyc", - "serial port c", - 0, - comc_probe, - comc_init, - comc_putchar, - comc_getchar, - comc_ischar, - NULL + .c_name = "ttyc", + .c_desc = "serial port c", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL }; struct console ttyd = { - "ttyd", - "serial port d", - 0, - comc_probe, - comc_init, - comc_putchar, - comc_getchar, - comc_ischar, - NULL + .c_name = "ttyd", + .c_desc = "serial port d", + .c_flags = 0, + .c_probe = comc_probe, + .c_init = comc_init, + .c_out = comc_putchar, + .c_in = comc_getchar, + .c_ready = comc_ischar, + .c_private = NULL }; static void @@ -140,9 +139,9 @@ comc_probe(struct console *cp) char value[20]; char *cons, *env; - if (cp->private == NULL) { - cp->private = malloc(sizeof(struct serial)); - port = cp->private; + if (cp->c_private == NULL) { + cp->c_private = malloc(sizeof(struct serial)); + port = cp->c_private; port->speed = COMSPEED; if (strcmp(cp->c_name, "ttya") == 0) @@ -168,18 +167,21 @@ comc_probe(struct console *cp) port->speed = comc_getspeed(port); } - snprintf(name, 20, "%s-mode", cp->c_name); + snprintf(name, sizeof (name), "%s-mode", cp->c_name); env = getenv(name); if (env != NULL) { (void) comc_parse_mode(port, env); } - env = comc_print_mode(port, value); + env = comc_asprint_mode(port); - unsetenv(name); - env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); + if (env != NULL) { + unsetenv(name); + env_setenv(name, EV_VOLATILE, env, comc_mode_set, env_nounset); + free(env); + } - snprintf(name, 20, "%s-ignore-cd", cp->c_name); + snprintf(name, sizeof (name), "%s-ignore-cd", cp->c_name); env = getenv(name); if (env != NULL) { if (strcmp(env, "true") == 0) @@ -188,11 +190,12 @@ comc_probe(struct console *cp) port->ignore_cd = 0; } - sprintf(value, "%s", port->ignore_cd? "true":"false"); + snprintf(value, sizeof (value), "%s", + port->ignore_cd? "true" : "false"); unsetenv(name); env_setenv(name, EV_VOLATILE, value, comc_cd_set, env_nounset); - snprintf(name, 20, "%s-rts-dtr-off", cp->c_name); + snprintf(name, sizeof (name), "%s-rts-dtr-off", cp->c_name); env = getenv(name); if (env != NULL) { if (strcmp(env, "true") == 0) @@ -201,11 +204,12 @@ comc_probe(struct console *cp) port->rtsdtr_off = 0; } - sprintf(value, "%s", port->rtsdtr_off? "true":"false"); + snprintf(value, sizeof (value), "%s", + port->rtsdtr_off? "true" : "false"); unsetenv(name); env_setenv(name, EV_VOLATILE, value, comc_rtsdtr_set, env_nounset); - snprintf(name, 20, "%s-pcidev", cp->c_name); + snprintf(name, sizeof (name), "%s-pcidev", cp->c_name); env = getenv(name); if (env != NULL) { port->locator = comc_parse_pcidev(env); @@ -235,7 +239,7 @@ static void comc_putchar(struct console *cp, int c) { int wait; - struct serial *sp = cp->private; + struct serial *sp = cp->c_private; for (wait = COMC_TXWAIT; wait > 0; wait--) if (inb(sp->ioaddr + com_lsr) & LSR_TXRDY) { @@ -247,21 +251,24 @@ comc_putchar(struct console *cp, int c) static int comc_getchar(struct console *cp) { - struct serial *sp = cp->private; + struct serial *sp = cp->c_private; return (comc_ischar(cp) ? inb(sp->ioaddr + com_data) : -1); } static int comc_ischar(struct console *cp) { - struct serial *sp = cp->private; + struct serial *sp = cp->c_private; return (inb(sp->ioaddr + com_lsr) & LSR_RXRDY); } static char * -comc_print_mode(struct serial *sp, char *buf) +comc_asprint_mode(struct serial *sp) { - char par; + char par, *buf; + + if (sp == NULL) + return (NULL); if ((sp->lcr & (PAREN|PAREVN)) == (PAREN|PAREVN)) par = 'e'; @@ -270,32 +277,35 @@ comc_print_mode(struct serial *sp, char *buf) else par = 'n'; - sprintf(buf, "%d,%d,%c,%d,-", sp->speed, + asprintf(&buf, "%d,%d,%c,%d,-", sp->speed, (sp->lcr & BITS8) == BITS8? 8:7, - par, (par & STOP2) == STOP2? 2:1); + par, (sp->lcr & STOP2) == STOP2? 2:1); return (buf); } static int comc_parse_mode(struct serial *sp, const char *value) { - int n; + unsigned long n; int speed; int lcr; char *ep; - n = strtol(value, &ep, 0); - if (n > 0) - speed = n; - else + if (value == NULL || *value == '\0') return (CMD_ERROR); - if (*ep == ',') - ep++; - else + errno = 0; + n = strtoul(value, &ep, 10); + if (errno != 0 || *ep != ',') + return (CMD_ERROR); + speed = n; + + ep++; + errno = 0; + n = strtoul(ep, &ep, 10); + if (errno != 0 || *ep != ',') return (CMD_ERROR); - n = strtol(ep, &ep, 0); switch (n) { case 7: lcr = BITS7; break; @@ -305,11 +315,7 @@ comc_parse_mode(struct serial *sp, const char *value) return (CMD_ERROR); } - if (*ep == ',') - ep++; - else - return (CMD_ERROR); - + ep++; switch (*ep++) { case 'n': break; @@ -387,7 +393,7 @@ comc_mode_set(struct env_var *ev, int flags, const void *value) if ((cp = get_console(ev->ev_name)) == NULL) return (CMD_ERROR); - if (comc_parse_mode(cp->private, value) == CMD_ERROR) + if (comc_parse_mode(cp->c_private, value) == CMD_ERROR) return (CMD_ERROR); comc_setup(cp); @@ -409,7 +415,7 @@ comc_cd_set(struct env_var *ev, int flags, const void *value) if ((cp = get_console(ev->ev_name)) == NULL) return (CMD_ERROR); - sp = cp->private; + sp = cp->c_private; if (strcmp(value, "true") == 0) sp->ignore_cd = 1; else if (strcmp(value, "false") == 0) @@ -436,7 +442,7 @@ comc_rtsdtr_set(struct env_var *ev, int flags, const void *value) if ((cp = get_console(ev->ev_name)) == NULL) return (CMD_ERROR); - sp = cp->private; + sp = cp->c_private; if (strcmp(value, "true") == 0) sp->rtsdtr_off = 1; else if (strcmp(value, "false") == 0) @@ -467,27 +473,28 @@ comc_parse_pcidev(const char *string) uint32_t locator; int pres; - pres = strtol(string, &p, 0); - if (p == string || *p != ':' || pres < 0 ) + errno = 0; + pres = strtoul(string, &p, 10); + if (errno != 0 || p == string || *p != ':' || pres < 0 ) return (0); bus = pres; p1 = ++p; - pres = strtol(p1, &p, 0); - if (p == string || *p != ':' || pres < 0 ) + pres = strtoul(p1, &p, 10); + if (errno != 0 || p == string || *p != ':' || pres < 0 ) return (0); dev = pres; p1 = ++p; - pres = strtol(p1, &p, 0); - if (p == string || (*p != ':' && *p != '\0') || pres < 0 ) + pres = strtoul(p1, &p, 10); + if (errno != 0 || p == string || (*p != ':' && *p != '\0') || pres < 0 ) return (0); func = pres; if (*p == ':') { p1 = ++p; - pres = strtol(p1, &p, 0); - if (p == string || *p != '\0' || pres <= 0 ) + pres = strtoul(p1, &p, 10); + if (errno != 0 || p == string || *p != '\0' || pres <= 0 ) return (0); bar = pres; } else @@ -506,7 +513,7 @@ comc_pcidev_handle(struct console *cp, uint32_t locator) (void)locator; return (CMD_ERROR); #else - struct serial *sp = cp->private; + struct serial *sp = cp->c_private; char intbuf[64]; uint32_t port; @@ -538,7 +545,7 @@ comc_pcidev_set(struct env_var *ev, int flags, const void *value) if ((cp = get_console(ev->ev_name)) == NULL) return (CMD_ERROR); - sp = cp->private; + sp = cp->c_private; if (value == NULL || (locator = comc_parse_pcidev(value)) <= 0) { printf("Invalid pcidev\n"); @@ -557,7 +564,7 @@ comc_pcidev_set(struct env_var *ev, int flags, const void *value) static void comc_setup(struct console *cp) { - struct serial *sp = cp->private; + struct serial *sp = cp->c_private; static int TRY_COUNT = 1000000; int tries; diff --git a/usr/src/boot/sys/boot/i386/libi386/nullconsole.c b/usr/src/boot/sys/boot/i386/libi386/nullconsole.c index cd9f3a514a..bef9841ffd 100644 --- a/usr/src/boot/sys/boot/i386/libi386/nullconsole.c +++ b/usr/src/boot/sys/boot/i386/libi386/nullconsole.c @@ -47,15 +47,15 @@ static int nullc_getchar(struct console *); static int nullc_ischar(struct console *); struct console nullconsole = { - "null", - "null port", - 0, - nullc_probe, - nullc_init, - nullc_putchar, - nullc_getchar, - nullc_ischar, - NULL + .c_name = "null", + .c_desc = "null port", + .c_flags = 0, + .c_probe = nullc_probe, + .c_init = nullc_init, + .c_out = nullc_putchar, + .c_in = nullc_getchar, + .c_ready = nullc_ischar, + .c_private = NULL }; static void diff --git a/usr/src/boot/sys/boot/i386/libi386/spinconsole.c b/usr/src/boot/sys/boot/i386/libi386/spinconsole.c index 762b88abd1..47a85da493 100644 --- a/usr/src/boot/sys/boot/i386/libi386/spinconsole.c +++ b/usr/src/boot/sys/boot/i386/libi386/spinconsole.c @@ -55,15 +55,15 @@ static int spinc_getchar(struct console *cp); static int spinc_ischar(struct console *cp); struct console spinconsole = { - "spin", - "spin port", - 0, - spinc_probe, - spinc_init, - spinc_putchar, - spinc_getchar, - spinc_ischar, - NULL + .c_name = "spin", + .c_desc = "spin port", + .c_flags = 0, + .c_probe = spinc_probe, + .c_init = spinc_init, + .c_out = spinc_putchar, + .c_in = spinc_getchar, + .c_ready = spinc_ischar, + .c_private = NULL }; static void diff --git a/usr/src/boot/sys/boot/ofw/libofw/ofw_console.c b/usr/src/boot/sys/boot/ofw/libofw/ofw_console.c index 59ce9a5067..a46c4a66ae 100644 --- a/usr/src/boot/sys/boot/ofw/libofw/ofw_console.c +++ b/usr/src/boot/sys/boot/ofw/libofw/ofw_console.c @@ -44,14 +44,15 @@ static ihandle_t stdin; static ihandle_t stdout; struct console ofwconsole = { - "ofw", - "Open Firmware console", - 0, - ofw_cons_probe, - ofw_cons_init, - ofw_cons_putchar, - ofw_cons_getchar, - ofw_cons_poll, + .c_name = "ofw", + .c_desc = "Open Firmware console", + .c_flags = 0, + .c_probe = ofw_cons_probe, + .c_init = ofw_cons_init, + .c_out = ofw_cons_putchar, + .c_in = ofw_cons_getchar, + .c_ready = ofw_cons_poll, + .c_private = NULL }; static void diff --git a/usr/src/boot/sys/boot/userboot/userboot/userboot_cons.c b/usr/src/boot/sys/boot/userboot/userboot/userboot_cons.c index 6f73ad5633..9b388e5f08 100644 --- a/usr/src/boot/sys/boot/userboot/userboot/userboot_cons.c +++ b/usr/src/boot/sys/boot/userboot/userboot/userboot_cons.c @@ -44,14 +44,15 @@ static int userboot_cons_getchar(void); static int userboot_cons_poll(void); struct console userboot_console = { - "userboot", - "userboot", - 0, - userboot_cons_probe, - userboot_cons_init, - userboot_cons_putchar, - userboot_cons_getchar, - userboot_cons_poll, + .c_name = "userboot", + .c_desc = "userboot", + .c_flags = 0, + .c_probe = userboot_cons_probe, + .c_init = userboot_cons_init, + .c_out = userboot_cons_putchar, + .c_in = userboot_cons_getchar, + .c_ready = userboot_cons_poll, + .c_private = NULL }; /* diff --git a/usr/src/cmd/beadm/Makefile b/usr/src/cmd/beadm/Makefile index 8ea7ac8cae..e19adc52bc 100644 --- a/usr/src/cmd/beadm/Makefile +++ b/usr/src/cmd/beadm/Makefile @@ -30,7 +30,7 @@ POFILE= beadm.po include ../Makefile.cmd -LDLIBS += -lnvpair -lbe +LDLIBS += -lnvpair -lbe -lcmdutils CERRWARN += -_gcc=-Wno-parentheses $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG diff --git a/usr/src/cmd/beadm/beadm.c b/usr/src/cmd/beadm/beadm.c index 00073ae717..3667c762a4 100644 --- a/usr/src/cmd/beadm/beadm.c +++ b/usr/src/cmd/beadm/beadm.c @@ -25,6 +25,7 @@ * Copyright 2015 Toomas Soome <tsoome@me.com> * Copyright 2015 Gary Mills * Copyright (c) 2015 by Delphix. All rights reserved. + * Copyright 2017 Jason King */ /* @@ -40,6 +41,8 @@ #include <stdlib.h> #include <wchar.h> #include <sys/types.h> +#include <sys/debug.h> +#include <libcmdutils.h> #include "libbe.h" @@ -55,6 +58,7 @@ #define DT_BUF_LEN (128) #define NUM_COLS (6) +CTASSERT(DT_BUF_LEN >= NN_NUMBUF_SZ); static int be_do_activate(int argc, char **argv); static int be_do_create(int argc, char **argv); @@ -249,32 +253,6 @@ init_hdr_cols(enum be_fmt be_fmt, struct hdr_info *hdr) } static void -nicenum(uint64_t num, char *buf, size_t buflen) -{ - uint64_t n = num; - int index = 0; - char u; - - while (n >= 1024) { - n /= 1024; - index++; - } - - u = " KMGTPE"[index]; - - if (index == 0) { - (void) snprintf(buf, buflen, "%llu", n); - } else { - int i; - for (i = 2; i >= 0; i--) { - if (snprintf(buf, buflen, "%.*f%c", i, - (double)num / (1ULL << 10 * index), u) <= 5) - break; - } - } -} - -static void count_widths(enum be_fmt be_fmt, struct hdr_info *hdr, be_node_list_t *be_nodes) { size_t len[NUM_COLS]; diff --git a/usr/src/cmd/du/du.c b/usr/src/cmd/du/du.c index 3911135b50..24ca7aca50 100644 --- a/usr/src/cmd/du/du.c +++ b/usr/src/cmd/du/du.c @@ -22,6 +22,7 @@ * Copyright 2017 OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2017 Jason King */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -66,9 +67,6 @@ static char *name; static size_t base_len = PATH_MAX + 1; /* # of chars for base */ static size_t name_len = PATH_MAX + 1; /* # of chars for name */ -#define NUMBER_WIDTH 64 -typedef char numbuf_t[NUMBER_WIDTH]; - /* * Output formats. illumos uses a tab as separator, XPG4 a space. */ @@ -528,75 +526,6 @@ descend(char *curname, int curfd, int *retcode, dev_t device) return (blocks); } -/* - * Convert an unsigned long long to a string representation and place the - * result in the caller-supplied buffer. - * The given number is in units of "unit_from" size, - * this will first be converted to a number in 1024 or 1000 byte size, - * depending on the scaling factor. - * Then the number is scaled down until it is small enough to be in a good - * human readable format i.e. in the range 0 thru scale-1. - * If it's smaller than 10 there's room enough to provide one decimal place. - * The value "(unsigned long long)-1" is a special case and is always - * converted to "-1". - * Returns a pointer to the caller-supplied buffer. - */ -static char * -number_to_scaled_string( - numbuf_t buf, /* put the result here */ - unsigned long long number, /* convert this number */ - unsigned long long unit_from, /* number of bytes per input unit */ - unsigned long long scale) /* 1024 (-h) or 1000 (-H) */ -{ - unsigned long long save = 0; - char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ - char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ - - if ((long long)number == (long long)-1) { - (void) strcpy(buf, "-1"); - return (buf); - } - - /* - * Convert number from unit_from to given scale (1024 or 1000) - * This means multiply number with unit_from and divide by scale. - * if number is large enough, we first divide and then multiply - * to avoid an overflow - * (large enough here means 100 (rather arbitrary value) - * times scale in order to reduce rounding errors) - * otherwise, we first multiply and then divide - * to avoid an underflow - */ - if (number >= 100L * scale) { - number = number / scale; - number = number * unit_from; - } else { - number = number * unit_from; - number = number / scale; - } - - /* - * Now we have number as a count of scale units. - * Stop scaling when we reached exa bytes, then something is - * probably wrong with our number. - */ - while ((number >= scale) && (*uom != 'E')) { - uom++; /* next unit of measurement */ - save = number; - number = (number + (scale / 2)) / scale; - } - - /* check if we should output a decimal place after the point */ - if (save && ((save / scale) < 10)) { - /* sprintf() will round for us */ - float fnum = (float)save / scale; - (void) sprintf(buf, "%4.1f%c", fnum, *uom); - } else { - (void) sprintf(buf, "%4llu%c", number, *uom); - } - return (buf); -} - static void printsize(blkcnt_t blocks, char *path) { @@ -605,11 +534,10 @@ printsize(blkcnt_t blocks, char *path) bsize = Aflg ? 1 : DEV_BSIZE; if (hflg) { - numbuf_t numbuf; - unsigned long long scale = 1024L; - (void) printf(FORMAT1, - number_to_scaled_string(numbuf, blocks, bsize, scale), - path); + char buf[NN_NUMBUF_SZ] = { 0 }; + + nicenum_scale(blocks, bsize, buf, sizeof (buf), 0); + (void) printf(FORMAT1, buf, path); } else if (kflg) { (void) printf(FORMAT2, (long long)kb(blocks), path); } else if (mflg) { diff --git a/usr/src/cmd/fs.d/Makefile b/usr/src/cmd/fs.d/Makefile index 54460a6084..8f8d752a94 100644 --- a/usr/src/cmd/fs.d/Makefile +++ b/usr/src/cmd/fs.d/Makefile @@ -88,6 +88,7 @@ CERRWARN += -_gcc=-Wno-unused-variable CERRWARN += -_gcc=-Wno-uninitialized CERRWARN += -_gcc=-Wno-unused-function +$(DFPROG) := LDLIBS += -lcmdutils $(SPPROG) := LDLIBS += -lkstat $(ROOTETCMNTTAB) := FILEMODE = 444 diff --git a/usr/src/cmd/fs.d/df.c b/usr/src/cmd/fs.d/df.c index 3ba4aeb832..ee7bf6e7d5 100644 --- a/usr/src/cmd/fs.d/df.c +++ b/usr/src/cmd/fs.d/df.c @@ -26,6 +26,9 @@ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * Copyright 2016 Jason King + */ #include <dlfcn.h> #include <stdio.h> @@ -48,7 +51,9 @@ #include <sys/mkdev.h> #include <sys/int_limits.h> #include <sys/zone.h> +#include <sys/debug.h> #include <libzfs.h> +#include <libcmdutils.h> #include "fslib.h" @@ -134,6 +139,7 @@ extern char *default_fstype(char *); /* message */ #define NUMBER_WIDTH 40 +CTASSERT(NUMBER_WIDTH >= NN_NUMBUF_SZ); /* * A numbuf_t is used when converting a number to a string representation @@ -219,7 +225,6 @@ static bool_int o_option; static bool_int tty_output; static bool_int use_scaling; -static int scale; static void usage(void); static void do_devnm(int, char **); @@ -591,7 +596,6 @@ parse_options(int argc, char *argv[]) SET_OPTION(g); } else if (arg == 'h') { use_scaling = TRUE; - scale = 1024; } else if (arg == 'k' && ! k_option) { SET_OPTION(k); } else if (arg == 'l' && ! l_option) { @@ -967,8 +971,7 @@ run_fs_specific_df(struct df_request request_list[], int entries) */ static int prune_list(struct df_request request_list[], - size_t n_requests, - size_t *valid_requests) + size_t n_requests, size_t *valid_requests) { size_t i; size_t n_valid = 0; @@ -1167,73 +1170,6 @@ number_to_string( } /* - * Convert an unsigned long long to a string representation and place the - * result in the caller-supplied buffer. - * The given number is in units of "unit_from" size, - * this will first be converted to a number in 1024 or 1000 byte size, - * depending on the scaling factor. - * Then the number is scaled down until it is small enough to be in a good - * human readable format i.e. in the range 0 thru scale-1. - * If it's smaller than 10 there's room enough to provide one decimal place. - * The value "(unsigned long long)-1" is a special case and is always - * converted to "-1". - * Returns a pointer to the caller-supplied buffer. - */ -static char * -number_to_scaled_string( - numbuf_t buf, /* put the result here */ - unsigned long long number, /* convert this number */ - int unit_from, - int scale) -{ - unsigned long long save = 0; - char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ - char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ - - if ((long long)number == (long long)-1) { - (void) strcpy(buf, "-1"); - return (buf); - } - - /* - * Convert number from unit_from to given scale (1024 or 1000). - * This means multiply number by unit_from and divide by scale. - * - * Would like to multiply by unit_from and then divide by scale, - * but if the first multiplication would overflow, then need to - * divide by scale and then multiply by unit_from. - */ - if (number > (UINT64_MAX / (unsigned long long)unit_from)) { - number = (number / (unsigned long long)scale) * - (unsigned long long)unit_from; - } else { - number = (number * (unsigned long long)unit_from) / - (unsigned long long)scale; - } - - /* - * Now we have number as a count of scale units. - * Stop scaling when we reached exa bytes, then something is - * probably wrong with our number. - */ - - while ((number >= scale) && (*uom != 'E')) { - uom++; /* next unit of measurement */ - save = number; - number = (number + (scale / 2)) / scale; - } - /* check if we should output a decimal place after the point */ - if (save && ((save / scale) < 10)) { - /* sprintf() will round for us */ - float fnum = (float)save / scale; - (void) sprintf(buf, "%2.1f%c", fnum, *uom); - } else { - (void) sprintf(buf, "%4llu%c", number, *uom); - } - return (buf); -} - -/* * The statvfs() implementation allows us to return only two values, the total * number of blocks and the number of blocks free. The equation 'used = total - * free' will not work for ZFS filesystems, due to the nature of pooled storage. @@ -1488,16 +1424,19 @@ k_output(struct df_request *dfrp, struct statvfs64 *fsp) adjust_total_blocks(dfrp, &total_blocks, fsp->f_frsize); if (use_scaling) { /* comes from the -h option */ - (void) printf("%-*s %*s %*s %*s %-*s %-s\n", - FILESYSTEM_WIDTH, file_system, - SCALED_WIDTH, number_to_scaled_string(total_blocks_buf, - total_blocks, fsp->f_frsize, scale), - SCALED_WIDTH, number_to_scaled_string(used_blocks_buf, - used_blocks, fsp->f_frsize, scale), - AVAILABLE_WIDTH, number_to_scaled_string(available_blocks_buf, - available_blocks, fsp->f_frsize, scale), - CAPACITY_WIDTH, capacity_buf, - DFR_MOUNT_POINT(dfrp)); + nicenum_scale(total_blocks, fsp->f_frsize, + total_blocks_buf, sizeof (total_blocks_buf), 0); + nicenum_scale(used_blocks, fsp->f_frsize, + used_blocks_buf, sizeof (used_blocks_buf), 0); + nicenum_scale(available_blocks, fsp->f_frsize, + available_blocks_buf, sizeof (available_blocks_buf), 0); + + (void) printf("%-*s %*s %*s %*s %-*s %-s\n", + FILESYSTEM_WIDTH, file_system, + SCALED_WIDTH, total_blocks_buf, + SCALED_WIDTH, used_blocks_buf, + AVAILABLE_WIDTH, available_blocks_buf, + CAPACITY_WIDTH, capacity_buf, DFR_MOUNT_POINT(dfrp)); return; } diff --git a/usr/src/cmd/ls/ls.c b/usr/src/cmd/ls/ls.c index 1adab128a4..876ea20117 100644 --- a/usr/src/cmd/ls/ls.c +++ b/usr/src/cmd/ls/ls.c @@ -26,7 +26,7 @@ */ /* - * Copyright 2009 Jason King. All rights reserved. + * Copyright 2017 Jason King. All rights reserved. * Use is subject to license terms. */ @@ -125,7 +125,6 @@ #undef BUFSIZ #define BUFSIZ 4096 -#define NUMBER_WIDTH 40 #define FMTSIZE 50 struct ditem { @@ -216,11 +215,6 @@ struct dchain { struct dchain *dc_next; /* next directory in the chain */ }; -/* - * A numbuf_t is used when converting a number to a string representation - */ -typedef char numbuf_t[NUMBER_WIDTH]; - static struct dchain *dfirst; /* start of the dir chain */ static struct dchain *cdfirst; /* start of the current dir chain */ static struct dchain *dtemp; /* temporary - used for linking */ @@ -252,9 +246,6 @@ static struct cachenode *findincache(struct cachenode **, long); static void csi_pprintf(unsigned char *); static void pprintf(char *, char *); static int compar(struct lbuf **pp1, struct lbuf **pp2); -static char *number_to_scaled_string(numbuf_t buf, - unsigned long long number, - long scale); static void record_ancestry(char *, struct stat *, struct lbuf *, int, struct ditem *); static void ls_color_init(void); @@ -306,7 +297,7 @@ static int atm; static int mtm; static int crtm; static int alltm; -static long hscale; +static uint_t nicenum_flags; static mode_t flags; static int err = 0; /* Contains return code */ static int colorflg; @@ -481,7 +472,7 @@ main(int argc, char *argv[]) if (strcmp(long_options[option_index].name, "si") == 0) { hflg++; - hscale = 1000; + nicenum_flags |= NN_DIVISOR_1000; continue; } @@ -751,7 +742,6 @@ main(int argc, char *argv[]) continue; case 'h': hflg++; - hscale = 1024; continue; case 'H': Hflg++; @@ -1255,7 +1245,6 @@ static void pentry(struct lbuf *ap) { struct lbuf *p; - numbuf_t hbuf; char *dmark = ""; /* Used if -p or -F option active */ char *cp; char *str; @@ -1308,9 +1297,13 @@ pentry(struct lbuf *ap) curcol += printf("%3u, %2u", (uint_t)major((dev_t)p->lsize), (uint_t)minor((dev_t)p->lsize)); - } else if (hflg && (p->lsize >= hscale)) { - curcol += printf("%7s", - number_to_scaled_string(hbuf, p->lsize, hscale)); + } else if (hflg) { + char numbuf[NN_NUMBUF_SZ]; + + nicenum_scale(p->lsize, 1, numbuf, sizeof (numbuf), + nicenum_flags); + + curcol += printf("%7s", numbuf); } else { uint64_t bsize = p->lsize / block_size; @@ -2444,85 +2437,6 @@ strcol(unsigned char *s1) return (w); } -/* - * Convert an unsigned long long to a string representation and place the - * result in the caller-supplied buffer. - * - * The number provided is a size in bytes. The number is first - * converted to an integral multiple of 'scale' bytes. This new - * number is then scaled down until it is small enough to be in a good - * human readable format, i.e. in the range 0 thru scale-1. If the - * number used to derive the final number is not a multiple of scale, and - * the final number has only a single significant digit, we compute - * tenths of units to provide a second significant digit. - * - * The value "(unsigned long long)-1" is a special case and is always - * converted to "-1". - * - * A pointer to the caller-supplied buffer is returned. - */ -static char * -number_to_scaled_string( - numbuf_t buf, /* put the result here */ - unsigned long long number, /* convert this number */ - long scale) -{ - unsigned long long save; - /* Measurement: kilo, mega, giga, tera, peta, exa */ - char *uom = "KMGTPE"; - - if ((long long)number == (long long)-1) { - (void) strlcpy(buf, "-1", sizeof (numbuf_t)); - return (buf); - } - - save = number; - number = number / scale; - - /* - * Now we have number as a count of scale units. - * If no further scaling is necessary, we round up as appropriate. - * - * The largest value number could have had entering the routine is - * 16 Exabytes, so running off the end of the uom array should - * never happen. We check for that, though, as a guard against - * a breakdown elsewhere in the algorithm. - */ - if (number < (unsigned long long)scale) { - if ((save % scale) >= (unsigned long long)(scale / 2)) { - if (++number == (unsigned long long)scale) { - uom++; - number = 1; - } - } - } else { - while ((number >= (unsigned long long)scale) && (*uom != 'E')) { - uom++; /* next unit of measurement */ - save = number; - /* - * If we're over half way to the next unit of - * 'scale' bytes (which means we should round - * up), then adding half of 'scale' prior to - * the division will push us into that next - * unit of scale when we perform the division - */ - number = (number + (scale / 2)) / scale; - } - } - - /* check if we should output a decimal place after the point */ - if ((save / scale) < 10) { - /* snprintf() will round for us */ - float fnum = (float)save / scale; - (void) snprintf(buf, sizeof (numbuf_t), "%2.1f%c", - fnum, *uom); - } else { - (void) snprintf(buf, sizeof (numbuf_t), "%4llu%c", - number, *uom); - } - return (buf); -} - /* Get extended system attributes and set the display */ int diff --git a/usr/src/cmd/stat/fsstat/Makefile b/usr/src/cmd/stat/fsstat/Makefile index 26bfdb023b..b86e2cc8f7 100644 --- a/usr/src/cmd/stat/fsstat/Makefile +++ b/usr/src/cmd/stat/fsstat/Makefile @@ -33,7 +33,7 @@ include $(SRC)/cmd/stat/Makefile.stat COMMON_OBJS = common.o timestamp.o COMMON_SRCS = $(COMMON_OBJS:%.o=$(STATCOMMONDIR)/%.c) -LDLIBS += -lkstat +LDLIBS += -lkstat -lcmdutils CFLAGS += $(CCVERBOSE) -I${STATCOMMONDIR} CERRWARN += -_gcc=-Wno-parentheses FILEMODE= 0555 diff --git a/usr/src/cmd/stat/fsstat/fsstat.c b/usr/src/cmd/stat/fsstat/fsstat.c index 5563f410ca..6bf07e333e 100644 --- a/usr/src/cmd/stat/fsstat/fsstat.c +++ b/usr/src/cmd/stat/fsstat/fsstat.c @@ -22,6 +22,7 @@ * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. + * Copyright 2017 Jason King */ #include <stdio.h> @@ -41,12 +42,14 @@ #include <sys/fstyp.h> #include <sys/fsid.h> #include <sys/mnttab.h> +#include <sys/debug.h> #include <values.h> #include <poll.h> #include <ctype.h> #include <libintl.h> #include <locale.h> #include <signal.h> +#include <libcmdutils.h> #include "statcommon.h" @@ -73,12 +76,7 @@ #define HEADERLINES 12 /* Number of lines between display headers */ #define LBUFSZ 64 /* Generic size for local buffer */ - -/* - * The following are used for the nicenum() function - */ -#define KILO_VAL 1024 -#define ONE_INDEX 3 +CTASSERT(LBUFSZ >= NN_NUMBUF_SZ); #define NENTITY_INIT 1 /* Initial number of entities to allocate */ @@ -112,9 +110,6 @@ typedef struct entity { #define ENTYPE_FSTYPE 1 #define ENTYPE_MNTPT 2 -/* If more sub-one units are added, make sure to adjust ONE_INDEX above */ -static char units[] = "num KMGTPE"; - char *cmdname; /* name of this command */ int caught_cont = 0; /* have caught a SIGCONT */ @@ -131,70 +126,14 @@ usage() exit(2); } -/* - * Given a 64-bit number and a starting unit (e.g., n - nanoseconds), - * convert the number to a 5-character representation including any - * decimal point and single-character unit. Put that representation - * into the array "buf" (which had better be big enough). - */ -char * -nicenum(uint64_t num, char unit, char *buf) -{ - uint64_t n = num; - int unit_index; - int index; - char u; - - /* If the user passed in a NUL/zero unit, use the blank value for 1 */ - if (unit == '\0') - unit = ' '; - - unit_index = 0; - while (units[unit_index] != unit) { - unit_index++; - if (unit_index > sizeof (units) - 1) { - (void) sprintf(buf, "??"); - return (buf); - } - } - - index = 0; - while (n >= KILO_VAL) { - n = (n + (KILO_VAL / 2)) / KILO_VAL; /* Round up or down */ - index++; - unit_index++; - } - - if (unit_index >= sizeof (units) - 1) { - (void) sprintf(buf, "??"); - return (buf); - } - - u = units[unit_index]; - - if (unit_index == ONE_INDEX) { - (void) sprintf(buf, "%llu", (u_longlong_t)n); - } else if (n < 10 && (num & (num - 1)) != 0) { - (void) sprintf(buf, "%.2f%c", - (double)num / (1ULL << 10 * index), u); - } else if (n < 100 && (num & (num - 1)) != 0) { - (void) sprintf(buf, "%.1f%c", - (double)num / (1ULL << 10 * index), u); - } else { - (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); - } - - return (buf); -} - - #define RAWVAL(ptr, member) ((ptr)->member.value.ui64) #define DELTA(member) \ (newvsp->member.value.ui64 - (oldvsp ? oldvsp->member.value.ui64 : 0)) -#define PRINTSTAT(isnice, nicestring, rawstring, rawval, unit, buf) \ +#define PRINTSTAT(isnice, nicestring, rawstring, rawval, buf) \ (isnice) ? \ - (void) printf((nicestring), nicenum(rawval, unit, buf)) \ + nicenum(rawval, buf, sizeof (buf)), \ + (void) printf((nicestring), (buf)) \ : \ (void) printf((rawstring), (rawval)) @@ -256,17 +195,17 @@ dflt_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) " file remov chng get set ops ops ops bytes ops bytes\n"); } - PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nnewfile, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nnamerm, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nnamechg, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nattrret, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nattrchg, buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", nlookup, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", nreaddir, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", ndataread, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", readthruput, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", ndatawrite, buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", writethruput, buf); (void) printf("%s\n", name); } @@ -282,17 +221,17 @@ io_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) " ops bytes ops bytes ops bytes ops ops\n"); } - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nread), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(read_bytes), buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nwrite), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(write_bytes), buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(readdir_bytes), buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nrwlock), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrwunlock), buf); (void) printf("%s\n", name); } @@ -307,12 +246,12 @@ vm_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) (void) printf(" map addmap delmap getpag putpag pagio\n"); } - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmap), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(naddmap), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ndelmap), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetpage), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nputpage), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(npageio), buf); (void) printf("%s\n", name); } @@ -326,10 +265,10 @@ attr_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) (void) printf("getattr setattr getsec setsec\n"); } - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), ' ', buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetattr), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetattr), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(ngetsecattr), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsetsecattr), buf); (void) printf("%s\n", name); } @@ -345,16 +284,16 @@ naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) "lookup creat remov link renam mkdir rmdir rddir symlnk rdlnk\n"); } - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), ' ', buf); - PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), ' ', buf); - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlookup), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(ncreate), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nremove), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nlink), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrename), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nmkdir), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nrmdir), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreaddir), buf); + PRINTSTAT(niceflag, " %5s ", "%lld:", DELTA(nsymlink), buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(nreadlink), buf); (void) printf("%s\n", name); } @@ -362,7 +301,7 @@ naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) #define PRINT_VOPSTAT_CMN(niceflag, vop) \ if (niceflag) \ (void) printf("%10s ", #vop); \ - PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), ' ', buf); + PRINTSTAT(niceflag, "%5s ", "%lld:", DELTA(n##vop), buf); #define PRINT_VOPSTAT(niceflag, vop) \ PRINT_VOPSTAT_CMN(niceflag, vop); \ @@ -372,7 +311,7 @@ naming_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) #define PRINT_VOPSTAT_IO(niceflag, vop) \ PRINT_VOPSTAT_CMN(niceflag, vop); \ PRINTSTAT(niceflag, " %5s\n", "%lld:", \ - DELTA(vop##_bytes), ' ', buf); + DELTA(vop##_bytes), buf); static void vop_display(char *name, vopstats_t *oldvsp, vopstats_t *newvsp, int dispflag) diff --git a/usr/src/cmd/swap/Makefile.com b/usr/src/cmd/swap/Makefile.com index 3734d8ac58..0514c15e62 100644 --- a/usr/src/cmd/swap/Makefile.com +++ b/usr/src/cmd/swap/Makefile.com @@ -43,7 +43,7 @@ CLEANFILES += $(OBJS) all: $(PROG) -LDLIBS += -ldiskmgt +LDLIBS += -ldiskmgt -lcmdutils $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) diff --git a/usr/src/cmd/swap/swap.c b/usr/src/cmd/swap/swap.c index 91cecd5cb4..6b4522745d 100644 --- a/usr/src/cmd/swap/swap.c +++ b/usr/src/cmd/swap/swap.c @@ -36,7 +36,9 @@ * contributors. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2017 Jason King. + */ /* * Swap administrative interface @@ -64,6 +66,8 @@ #include <libintl.h> #include <libdiskmgt.h> #include <sys/fs/zfs.h> +#include <libcmdutils.h> +#include <sys/debug.h> #define LFLAG 0x01 /* swap -l (list swap devices) */ #define DFLAG 0x02 /* swap -d (delete swap device) */ @@ -75,6 +79,8 @@ #define KFLAG 0x80 /* swap -k (size in kilobytes) */ #define NUMBER_WIDTH 64 +/* make sure nicenum_scale() has enough space */ +CTASSERT(NUMBER_WIDTH >= NN_NUMBUF_SZ); typedef char numbuf_t[NUMBER_WIDTH]; static char *prognamep; @@ -85,9 +91,6 @@ static void usage(void); static int doswap(int flag); static int valid(char *, off_t, off_t); static int list(int flag); -static char *number_to_scaled_string(numbuf_t buf, unsigned long long number, - unsigned long long unit_from, unsigned long long scale); - int main(int argc, char **argv) @@ -253,7 +256,7 @@ main(int argc, char **argv) } if ((ret = valid(pathname, s_offset * 512, length * 512)) == 0) { - ret = add(pathname, s_offset, length, flag); + ret = add(pathname, s_offset, length, flag); } } if (!(flag & ~HFLAG & ~KFLAG)) { @@ -271,16 +274,16 @@ usage(void) (void) fprintf(stderr, gettext("Usage:\t%s -l\n"), prognamep); (void) fprintf(stderr, gettext("\tsub option :\n")); (void) fprintf(stderr, gettext("\t\t-h : displays size in human " - "readable format\n")); + "readable format\n")); (void) fprintf(stderr, gettext("\t\t-k : displays size in KB\n")); (void) fprintf(stderr, "\t%s -s\n", prognamep); (void) fprintf(stderr, gettext("\tsub option :\n")); (void) fprintf(stderr, gettext("\t\t-h : displays size in human " - "readable format rather than KB\n")); + "readable format rather than KB\n")); (void) fprintf(stderr, gettext("\t%s -d <file name> [low block]\n"), - prognamep); + prognamep); (void) fprintf(stderr, gettext("\t%s -a <file name> [low block]" - " [nbr of blocks]\n"), prognamep); + " [nbr of blocks]\n"), prognamep); } /* @@ -305,7 +308,6 @@ doswap(int flag) struct anoninfo ai; pgcnt_t allocated, reserved, available; numbuf_t numbuf; - unsigned long long scale = 1024L; /* * max = total amount of swap space including physical memory @@ -339,24 +341,25 @@ doswap(int flag) if (flag & HFLAG) { int factor = (int)(sysconf(_SC_PAGESIZE)); - (void) printf(gettext("total: %s allocated + "), - number_to_scaled_string(numbuf, allocated, - factor, scale)); - (void) printf(gettext("%s reserved = "), - number_to_scaled_string(numbuf, reserved, - factor, scale)); - (void) printf(gettext("%s used, "), - number_to_scaled_string(numbuf, - allocated + reserved, factor, scale)); - (void) printf(gettext("%s available\n"), - number_to_scaled_string(numbuf, available, - factor, scale)); + + nicenum_scale(allocated, factor, numbuf, sizeof (numbuf), 0); + (void) printf(gettext("total: %s allocated + "), numbuf); + + nicenum_scale(reserved, factor, numbuf, sizeof (numbuf), 0); + (void) printf(gettext("%s reserved = "), numbuf); + + nicenum_scale(allocated + reserved, factor, numbuf, + sizeof (numbuf), 0); + (void) printf(gettext("%s used, "), numbuf); + + nicenum_scale(available, factor, numbuf, sizeof (numbuf), 0); + (void) printf(gettext("%s available\n"), numbuf); } else { (void) printf(gettext("total: %luk bytes allocated + %luk" - " reserved = %luk used, %luk available\n"), - ctok(allocated), ctok(reserved), - ctok(reserved) + ctok(allocated), - ctok(available)); + " reserved = %luk used, %luk available\n"), + ctok(allocated), ctok(reserved), + ctok(reserved) + ctok(allocated), + ctok(available)); } return (0); @@ -373,7 +376,6 @@ list(int flag) char fullpath[MAXPATHLEN+1]; int num; numbuf_t numbuf; - unsigned long long scale = 1024L; if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { perror(prognamep); @@ -387,13 +389,13 @@ list(int flag) if ((st = malloc(num * sizeof (swapent_t) + sizeof (int))) == NULL) { (void) fprintf(stderr, - gettext("Malloc failed. Please try later.\n")); + gettext("Malloc failed. Please try later.\n")); perror(prognamep); return (2); } if ((path = malloc(num * MAXPATHLEN)) == NULL) { (void) fprintf(stderr, - gettext("Malloc failed. Please try later.\n")); + gettext("Malloc failed. Please try later.\n")); perror(prognamep); return (2); } @@ -431,17 +433,17 @@ list(int flag) for (i = 0; i < num; i++, swapent++) { if (*swapent->ste_path != '/') (void) snprintf(fullpath, sizeof (fullpath), - "/dev/%s", swapent->ste_path); + "/dev/%s", swapent->ste_path); else (void) snprintf(fullpath, sizeof (fullpath), - "%s", swapent->ste_path); + "%s", swapent->ste_path); if (stat64(fullpath, &statbuf) < 0) if (*swapent->ste_path != '/') (void) printf(gettext("%-20s - "), - swapent->ste_path); + swapent->ste_path); else (void) printf(gettext("%-20s ?,? "), - fullpath); + fullpath); else { if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { @@ -455,34 +457,31 @@ list(int flag) } { int diskblks_per_page = - (int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); + (int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); if (flag & HFLAG) { - (void) printf(gettext(" %8s"), - number_to_scaled_string(numbuf, - swapent->ste_start, DEV_BSIZE, - scale)); - (void) printf(gettext(" %8s"), - number_to_scaled_string(numbuf, - swapent->ste_pages * - diskblks_per_page, - DEV_BSIZE, scale)); - (void) printf(gettext(" %8s"), - number_to_scaled_string(numbuf, - swapent->ste_free * - diskblks_per_page, - DEV_BSIZE, scale)); + nicenum_scale(swapent->ste_start, DEV_BSIZE, numbuf, + sizeof (numbuf), 0); + (void) printf(gettext(" %8s"), numbuf); + + nicenum_scale(swapent->ste_pages, DEV_BSIZE * + diskblks_per_page, numbuf, sizeof (numbuf), 0); + (void) printf(gettext(" %8s"), numbuf); + + nicenum_scale(swapent->ste_free, DEV_BSIZE * + diskblks_per_page, numbuf, sizeof (numbuf), 0); + (void) printf(gettext(" %8s"), numbuf); } else if (flag & KFLAG) { (void) printf(gettext(" %7luK %7luK %7luK"), - swapent->ste_start * DEV_BSIZE / 1024, - swapent->ste_pages * diskblks_per_page * - DEV_BSIZE / 1024, - swapent->ste_free * diskblks_per_page * - DEV_BSIZE / 1024); + swapent->ste_start * DEV_BSIZE / 1024, + swapent->ste_pages * diskblks_per_page * + DEV_BSIZE / 1024, + swapent->ste_free * diskblks_per_page * + DEV_BSIZE / 1024); } else { (void) printf(gettext(" %8lu %8lu %8lu"), - swapent->ste_start, - swapent->ste_pages * diskblks_per_page, - swapent->ste_free * diskblks_per_page); + swapent->ste_start, + swapent->ste_pages * diskblks_per_page, + swapent->ste_free * diskblks_per_page); } } if (swapent->ste_flags & ST_INDEL) @@ -493,64 +492,6 @@ list(int flag) return (0); } -/* Copied from du.c */ -static char * -number_to_scaled_string( - numbuf_t buf, /* put the result here */ - unsigned long long number, /* convert this number */ - unsigned long long unit_from, /* number of byes per input unit */ - unsigned long long scale) /* 1024 (-h) or 1000 (-H) */ -{ - unsigned long long save = 0; - char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ - char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ - - if ((long long)number == (long long) -1) { - (void) strcpy(buf, "-1"); - return (buf); - } - - /* - * Convert number from unit_from to given scale (1024 or 1000) - * This means multiply number with unit_from and divide by scale. - * if number is large enough, we first divide and then multiply - * to avoid an overflow (large enough here means 100 (rather arbitrary - * value) times scale in order to reduce rounding errors) - * otherwise, we first multiply and then divide to avoid an underflow. - */ - if (number >= 100L * scale) { - number = number / scale; - number = number * unit_from; - } else { - number = number * unit_from; - number = number / scale; - } - - /* - * Now we have number as a count of scale units. - * Stop scaling when we reached exa bytes, then something is - * probably wrong with our number. - */ - while ((number >= scale) && (*uom != 'E')) { - uom++; /* Next unit of measurement */ - save = number; - number = (number + (scale / 2)) / scale; - } - - /* Check if we should output a decimal place after the point */ - if (save && ((save / scale) < 10)) { - /* sprintf() will round for us */ - float fnum = (float)save / scale; - (void) sprintf(buf, "%.1f%c", fnum, *uom); - } else { - (void) sprintf(buf, "%llu%c", number, *uom); - } - return (buf); -} - - - - static void dumpadm_err(const char *warning) { @@ -701,7 +642,7 @@ add(char *path, off_t offset, off_t cnt, int flags) statvfs("/etc", &fsb) == 0 && !(fsb.f_flag & ST_RDONLY)) { (void) printf( - gettext("operating system crash dump was previously " + gettext("operating system crash dump was previously " "disabled --\ninvoking dumpadm(1M) -d swap to select " "new dump device\n")); @@ -726,7 +667,7 @@ valid(char *pathname, off_t offset, off_t length) } if (!((S_ISREG(f.st_mode) && (f.st_mode & S_ISVTX) == S_ISVTX) || - S_ISBLK(f.st_mode))) { + S_ISBLK(f.st_mode))) { (void) fprintf(stderr, gettext("\"%s\" is not valid for swapping.\n" "It must be a block device or a regular file with the\n" @@ -753,8 +694,8 @@ valid(char *pathname, off_t offset, off_t length) if (offset < 0) { (void) fprintf(stderr, - gettext("%s: low block is invalid\n"), - pathname); + gettext("%s: low block is invalid\n"), + pathname); return (EINVAL); } diff --git a/usr/src/cmd/zdb/Makefile.com b/usr/src/cmd/zdb/Makefile.com index 63b8366c80..022c984dbe 100644 --- a/usr/src/cmd/zdb/Makefile.com +++ b/usr/src/cmd/zdb/Makefile.com @@ -23,6 +23,7 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright (c) 2012 by Delphix. All rights reserved. +# Copyright 2017 Joyent, Inc. # PROG:sh= cd ..; basename `pwd` @@ -36,7 +37,7 @@ INCS += -I../../../lib/libzpool/common INCS += -I../../../uts/common/fs/zfs INCS += -I../../../common/zfs -LDLIBS += -lzpool -lumem -lnvpair -lzfs -lavl +LDLIBS += -lzpool -lumem -lnvpair -lzfs -lavl -lcmdutils C99MODE= -xc99=%all C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c index a2e0a67e11..5dcb12f5bf 100644 --- a/usr/src/cmd/zdb/zdb.c +++ b/usr/src/cmd/zdb/zdb.c @@ -63,6 +63,7 @@ #include <sys/abd.h> #include <sys/blkptr.h> #include <zfs_comutil.h> +#include <libcmdutils.h> #undef verify #include <libzfs.h> @@ -276,12 +277,12 @@ dump_history_offsets(objset_t *os, uint64_t object, void *data, size_t size) } static void -zdb_nicenum(uint64_t num, char *buf) +zdb_nicenum(uint64_t num, char *buf, size_t buflen) { if (dump_opt['P']) - (void) sprintf(buf, "%llu", (longlong_t)num); + (void) snprintf(buf, buflen, "%llu", (longlong_t)num); else - nicenum(num, buf); + nicenum(num, buf, sizeof (buf)); } const char histo_stars[] = "****************************************"; @@ -458,12 +459,17 @@ dump_bpobj(objset_t *os, uint64_t object, void *data, size_t size) bpobj_phys_t *bpop = data; char bytes[32], comp[32], uncomp[32]; + /* make sure the output won't get truncated */ + CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); + if (bpop == NULL) return; - zdb_nicenum(bpop->bpo_bytes, bytes); - zdb_nicenum(bpop->bpo_comp, comp); - zdb_nicenum(bpop->bpo_uncomp, uncomp); + zdb_nicenum(bpop->bpo_bytes, bytes, sizeof (bytes)); + zdb_nicenum(bpop->bpo_comp, comp, sizeof (comp)); + zdb_nicenum(bpop->bpo_uncomp, uncomp, sizeof (uncomp)); (void) printf("\t\tnum_blkptrs = %llu\n", (u_longlong_t)bpop->bpo_num_blkptrs); @@ -756,7 +762,10 @@ dump_metaslab_stats(metaslab_t *msp) avl_tree_t *t = &msp->ms_size_tree; int free_pct = range_tree_space(rt) * 100 / msp->ms_size; - zdb_nicenum(metaslab_block_maxsize(msp), maxbuf); + /* max sure nicenum has enough space */ + CTASSERT(sizeof (maxbuf) >= NN_NUMBUF_SZ); + + zdb_nicenum(metaslab_block_maxsize(msp), maxbuf, sizeof (maxbuf)); (void) printf("\t %25s %10lu %7s %6s %4s %4d%%\n", "segments", avl_numnodes(t), "maxsize", maxbuf, @@ -773,7 +782,8 @@ dump_metaslab(metaslab_t *msp) space_map_t *sm = msp->ms_sm; char freebuf[32]; - zdb_nicenum(msp->ms_size - space_map_allocated(sm), freebuf); + zdb_nicenum(msp->ms_size - space_map_allocated(sm), freebuf, + sizeof (freebuf)); (void) printf( "\tmetaslab %6llu offset %12llx spacemap %6llu free %5s\n", @@ -1331,6 +1341,9 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) time_t crtime; char nice[32]; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (nice) >= NN_NUMBUF_SZ); + if (dd == NULL) return; @@ -1346,15 +1359,15 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) (u_longlong_t)dd->dd_origin_obj); (void) printf("\t\tchild_dir_zapobj = %llu\n", (u_longlong_t)dd->dd_child_dir_zapobj); - zdb_nicenum(dd->dd_used_bytes, nice); + zdb_nicenum(dd->dd_used_bytes, nice, sizeof (nice)); (void) printf("\t\tused_bytes = %s\n", nice); - zdb_nicenum(dd->dd_compressed_bytes, nice); + zdb_nicenum(dd->dd_compressed_bytes, nice, sizeof (nice)); (void) printf("\t\tcompressed_bytes = %s\n", nice); - zdb_nicenum(dd->dd_uncompressed_bytes, nice); + zdb_nicenum(dd->dd_uncompressed_bytes, nice, sizeof (nice)); (void) printf("\t\tuncompressed_bytes = %s\n", nice); - zdb_nicenum(dd->dd_quota, nice); + zdb_nicenum(dd->dd_quota, nice, sizeof (nice)); (void) printf("\t\tquota = %s\n", nice); - zdb_nicenum(dd->dd_reserved, nice); + zdb_nicenum(dd->dd_reserved, nice, sizeof (nice)); (void) printf("\t\treserved = %s\n", nice); (void) printf("\t\tprops_zapobj = %llu\n", (u_longlong_t)dd->dd_props_zapobj); @@ -1364,7 +1377,8 @@ dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) (u_longlong_t)dd->dd_flags); #define DO(which) \ - zdb_nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice); \ + zdb_nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice, \ + sizeof (nice)); \ (void) printf("\t\tused_breakdown[" #which "] = %s\n", nice) DO(HEAD); DO(SNAP); @@ -1383,15 +1397,22 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size) char used[32], compressed[32], uncompressed[32], unique[32]; char blkbuf[BP_SPRINTF_LEN]; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (used) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (compressed) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (uncompressed) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (unique) >= NN_NUMBUF_SZ); + if (ds == NULL) return; ASSERT(size == sizeof (*ds)); crtime = ds->ds_creation_time; - zdb_nicenum(ds->ds_referenced_bytes, used); - zdb_nicenum(ds->ds_compressed_bytes, compressed); - zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed); - zdb_nicenum(ds->ds_unique_bytes, unique); + zdb_nicenum(ds->ds_referenced_bytes, used, sizeof (used)); + zdb_nicenum(ds->ds_compressed_bytes, compressed, sizeof (compressed)); + zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed, + sizeof (uncompressed)); + zdb_nicenum(ds->ds_unique_bytes, unique, sizeof (unique)); snprintf_blkptr(blkbuf, sizeof (blkbuf), &ds->ds_bp); (void) printf("\t\tdir_obj = %llu\n", @@ -1450,12 +1471,15 @@ dump_bptree(objset_t *os, uint64_t obj, char *name) bptree_phys_t *bt; dmu_buf_t *db; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); + if (dump_opt['d'] < 3) return; VERIFY3U(0, ==, dmu_bonus_hold(os, obj, FTAG, &db)); bt = db->db_data; - zdb_nicenum(bt->bt_bytes, bytes); + zdb_nicenum(bt->bt_bytes, bytes, sizeof (bytes)); (void) printf("\n %s: %llu datasets, %s\n", name, (unsigned long long)(bt->bt_end - bt->bt_begin), bytes); dmu_buf_rele(db, FTAG); @@ -1487,13 +1511,18 @@ dump_full_bpobj(bpobj_t *bpo, char *name, int indent) char comp[32]; char uncomp[32]; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); + if (dump_opt['d'] < 3) return; - zdb_nicenum(bpo->bpo_phys->bpo_bytes, bytes); + zdb_nicenum(bpo->bpo_phys->bpo_bytes, bytes, sizeof (bytes)); if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) { - zdb_nicenum(bpo->bpo_phys->bpo_comp, comp); - zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp); + zdb_nicenum(bpo->bpo_phys->bpo_comp, comp, sizeof (comp)); + zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp, sizeof (uncomp)); (void) printf(" %*s: object %llu, %llu local blkptrs, " "%llu subobjs in object %llu, %s (%s/%s comp)\n", indent * 8, name, @@ -1547,6 +1576,11 @@ dump_deadlist(dsl_deadlist_t *dl) char comp[32]; char uncomp[32]; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); + if (dump_opt['d'] < 3) return; @@ -1555,9 +1589,9 @@ dump_deadlist(dsl_deadlist_t *dl) return; } - zdb_nicenum(dl->dl_phys->dl_used, bytes); - zdb_nicenum(dl->dl_phys->dl_comp, comp); - zdb_nicenum(dl->dl_phys->dl_uncomp, uncomp); + zdb_nicenum(dl->dl_phys->dl_used, bytes, sizeof (bytes)); + zdb_nicenum(dl->dl_phys->dl_comp, comp, sizeof (comp)); + zdb_nicenum(dl->dl_phys->dl_uncomp, uncomp, sizeof (uncomp)); (void) printf("\n Deadlist: %s (%s/%s comp)\n", bytes, comp, uncomp); @@ -1860,6 +1894,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) char aux[50]; int error; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (iblk) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (dblk) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (bonus_size) >= NN_NUMBUF_SZ); + if (*print_header) { (void) printf("\n%10s %3s %5s %5s %5s %5s %6s %s\n", "Object", "lvl", "iblk", "dblk", "dsize", "lsize", @@ -1880,11 +1921,11 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) } dmu_object_info_from_dnode(dn, &doi); - zdb_nicenum(doi.doi_metadata_block_size, iblk); - zdb_nicenum(doi.doi_data_block_size, dblk); - zdb_nicenum(doi.doi_max_offset, lsize); - zdb_nicenum(doi.doi_physical_blocks_512 << 9, asize); - zdb_nicenum(doi.doi_bonus_size, bonus_size); + zdb_nicenum(doi.doi_metadata_block_size, iblk, sizeof (iblk)); + zdb_nicenum(doi.doi_data_block_size, dblk, sizeof (dblk)); + zdb_nicenum(doi.doi_max_offset, lsize, sizeof (lsize)); + zdb_nicenum(doi.doi_physical_blocks_512 << 9, asize, sizeof (asize)); + zdb_nicenum(doi.doi_bonus_size, bonus_size, sizeof (bonus_size)); (void) sprintf(fill, "%6.2f", 100.0 * doi.doi_fill_count * doi.doi_data_block_size / (object == 0 ? DNODES_PER_BLOCK : 1) / doi.doi_max_offset); @@ -1947,6 +1988,8 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) for (;;) { char segsize[32]; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (segsize) >= NN_NUMBUF_SZ); error = dnode_next_offset(dn, 0, &start, minlvl, blkfill, 0); if (error) @@ -1954,7 +1997,7 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header) end = start; error = dnode_next_offset(dn, DNODE_FIND_HOLE, &end, minlvl, blkfill, 0); - zdb_nicenum(end - start, segsize); + zdb_nicenum(end - start, segsize, sizeof (segsize)); (void) printf("\t\tsegment [%016llx, %016llx)" " size %5s\n", (u_longlong_t)start, (u_longlong_t)end, segsize); @@ -1985,6 +2028,9 @@ dump_dir(objset_t *os) int print_header = 1; int i, error; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ); + dsl_pool_config_enter(dmu_objset_pool(os), FTAG); dmu_objset_fast_stat(os, &dds); dsl_pool_config_exit(dmu_objset_pool(os), FTAG); @@ -2003,7 +2049,7 @@ dump_dir(objset_t *os) ASSERT3U(usedobjs, ==, BP_GET_FILL(os->os_rootbp)); - zdb_nicenum(refdbytes, numbuf); + zdb_nicenum(refdbytes, numbuf, sizeof (numbuf)); if (verbosity >= 4) { (void) snprintf(blkbuf, sizeof (blkbuf), ", rootbp "); @@ -2648,6 +2694,9 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, int sec_remaining = (zcb->zcb_totalasize - bytes) / 1024 / kb_per_sec; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (buf) >= NN_NUMBUF_SZ); + zfs_nicenum(bytes, buf, sizeof (buf)); (void) fprintf(stderr, "\r%5s completed (%4dMB/s) " @@ -2997,6 +3046,14 @@ dump_block_stats(spa_t *spa) char avg[32], gang[32]; char *typename; + /* make sure nicenum has enough space */ + CTASSERT(sizeof (csize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (psize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (avg) >= NN_NUMBUF_SZ); + CTASSERT(sizeof (gang) >= NN_NUMBUF_SZ); + if (t < DMU_OT_NUMTYPES) typename = dmu_ot[t].ot_name; else @@ -3030,12 +3087,17 @@ dump_block_stats(spa_t *spa) zcb.zcb_type[ZB_TOTAL][t].zb_asize) continue; - zdb_nicenum(zb->zb_count, csize); - zdb_nicenum(zb->zb_lsize, lsize); - zdb_nicenum(zb->zb_psize, psize); - zdb_nicenum(zb->zb_asize, asize); - zdb_nicenum(zb->zb_asize / zb->zb_count, avg); - zdb_nicenum(zb->zb_gangs, gang); + zdb_nicenum(zb->zb_count, csize, + sizeof (csize)); + zdb_nicenum(zb->zb_lsize, lsize, + sizeof (lsize)); + zdb_nicenum(zb->zb_psize, psize, + sizeof (psize)); + zdb_nicenum(zb->zb_asize, asize, + sizeof (asize)); + zdb_nicenum(zb->zb_asize / zb->zb_count, avg, + sizeof (avg)); + zdb_nicenum(zb->zb_gangs, gang, sizeof (gang)); (void) printf("%6s\t%5s\t%5s\t%5s\t%5s" "\t%5.2f\t%6.2f\t", diff --git a/usr/src/cmd/ztest/zloop.bash b/usr/src/cmd/ztest/zloop.bash index 7203356d3d..2410eaa985 100755 --- a/usr/src/cmd/ztest/zloop.bash +++ b/usr/src/cmd/ztest/zloop.bash @@ -189,6 +189,7 @@ while [[ $timeout -eq 0 ]] || [[ $curtime -le $(($starttime + $timeout)) ]]; do $BIN/$cmd >>ztest.out 2>&1 ztrc=$? /bin/egrep '===|WARNING' ztest.out >>ztest.history + $SBIN/zdb -U $workdir/zpool.cache -DD ztest >>ztest.ddt 2>&1 store_core diff --git a/usr/src/cmd/ztest/ztest.c b/usr/src/cmd/ztest/ztest.c index b8264b3105..32ecef6b18 100644 --- a/usr/src/cmd/ztest/ztest.c +++ b/usr/src/cmd/ztest/ztest.c @@ -24,6 +24,7 @@ * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] + * Copyright 2017 Joyent, Inc. */ /* @@ -123,6 +124,7 @@ #include <math.h> #include <sys/fs/zfs.h> #include <libnvpair.h> +#include <libcmdutils.h> static int ztest_fd_data = -1; static int ztest_fd_rand = -1; @@ -554,12 +556,13 @@ usage(boolean_t requested) { const ztest_shared_opts_t *zo = &ztest_opts_defaults; - char nice_vdev_size[10]; - char nice_gang_bang[10]; + char nice_vdev_size[NN_NUMBUF_SZ]; + char nice_gang_bang[NN_NUMBUF_SZ]; FILE *fp = requested ? stdout : stderr; - nicenum(zo->zo_vdev_size, nice_vdev_size); - nicenum(zo->zo_metaslab_gang_bang, nice_gang_bang); + nicenum(zo->zo_vdev_size, nice_vdev_size, sizeof (nice_vdev_size)); + nicenum(zo->zo_metaslab_gang_bang, nice_gang_bang, + sizeof (nice_gang_bang)); (void) fprintf(fp, "Usage: %s\n" "\t[-v vdevs (default: %llu)]\n" @@ -1829,13 +1832,14 @@ ztest_get_done(zgd_t *zgd, int error) ztest_object_unlock(zd, object); if (error == 0 && zgd->zgd_bp) - zil_add_block(zgd->zgd_zilog, zgd->zgd_bp); + zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp); umem_free(zgd, sizeof (*zgd)); } static int -ztest_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) +ztest_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, + zio_t *zio) { ztest_ds_t *zd = arg; objset_t *os = zd->zd_os; @@ -1849,6 +1853,10 @@ ztest_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) zgd_t *zgd; int error; + ASSERT3P(lwb, !=, NULL); + ASSERT3P(zio, !=, NULL); + ASSERT3U(size, !=, 0); + ztest_object_lock(zd, object, RL_READER); error = dmu_bonus_hold(os, object, FTAG, &db); if (error) { @@ -1869,7 +1877,7 @@ ztest_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) db = NULL; zgd = umem_zalloc(sizeof (*zgd), UMEM_NOFAIL); - zgd->zgd_zilog = zd->zd_zilog; + zgd->zgd_lwb = lwb; zgd->zgd_private = zd; if (buf != NULL) { /* immediate write */ @@ -3151,10 +3159,10 @@ ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id) old_class_space, new_class_space); if (ztest_opts.zo_verbose >= 5) { - char oldnumbuf[6], newnumbuf[6]; + char oldnumbuf[NN_NUMBUF_SZ], newnumbuf[NN_NUMBUF_SZ]; - nicenum(old_class_space, oldnumbuf); - nicenum(new_class_space, newnumbuf); + nicenum(old_class_space, oldnumbuf, sizeof (oldnumbuf)); + nicenum(new_class_space, newnumbuf, sizeof (newnumbuf)); (void) printf("%s grew from %s to %s\n", spa->spa_name, oldnumbuf, newnumbuf); } @@ -6196,7 +6204,7 @@ main(int argc, char **argv) ztest_info_t *zi; ztest_shared_callstate_t *zc; char timebuf[100]; - char numbuf[6]; + char numbuf[NN_NUMBUF_SZ]; spa_t *spa; char *cmd; boolean_t hasalt; @@ -6333,7 +6341,7 @@ main(int argc, char **argv) now = MIN(now, zs->zs_proc_stop); print_time(zs->zs_proc_stop - now, timebuf); - nicenum(zs->zs_space, numbuf); + nicenum(zs->zs_space, numbuf, sizeof (numbuf)); (void) printf("Pass %3d, %8s, %3llu ENOSPC, " "%4.1f%% of %5s used, %3.0f%% done, %8s to go\n", diff --git a/usr/src/lib/libcmdutils/Makefile.com b/usr/src/lib/libcmdutils/Makefile.com index ffe3c9cba5..9cb35f8795 100644 --- a/usr/src/lib/libcmdutils/Makefile.com +++ b/usr/src/lib/libcmdutils/Makefile.com @@ -26,7 +26,7 @@ LIBRARY= libcmdutils.a VERS= .1 CMD_OBJS= avltree.o sysattrs.o writefile.o process_xattrs.o uid.o gid.o \ - custr.o + custr.o nicenum.o COM_OBJS= list.o OBJECTS= $(CMD_OBJS) $(COM_OBJS) diff --git a/usr/src/lib/libcmdutils/common/mapfile-vers b/usr/src/lib/libcmdutils/common/mapfile-vers index 1f63d4eee0..0ac77eb010 100644 --- a/usr/src/lib/libcmdutils/common/mapfile-vers +++ b/usr/src/lib/libcmdutils/common/mapfile-vers @@ -72,6 +72,8 @@ SYMBOL_VERSION SUNWprivate_1.1 { list_remove_head; list_remove_tail; list_tail; + nicenum; + nicenum_scale; tnode_compare; sysattr_type; sysattr_support; diff --git a/usr/src/lib/libcmdutils/common/nicenum.c b/usr/src/lib/libcmdutils/common/nicenum.c new file mode 100644 index 0000000000..8e3202f792 --- /dev/null +++ b/usr/src/lib/libcmdutils/common/nicenum.c @@ -0,0 +1,130 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Jason king + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/debug.h> +#include "libcmdutils.h" + +/* The largest suffix that can fit, aka an exabyte (2^60 / 10^18) */ +#define INDEX_MAX (6) + +/* Verify INDEX_MAX fits */ +CTASSERT(INDEX_MAX * 10 < sizeof (uint64_t) * 8); + +void +nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen, + uint32_t flags) +{ + uint64_t divamt = 1024; + uint64_t divisor = 1; + int index = 0; + int rc = 0; + char u; + + if (units == 0) + units = 1; + + if (n > 0) { + n *= units; + if (n < units) + goto overflow; + } + + if (flags & NN_DIVISOR_1000) + divamt = 1000; + + /* + * This tries to find the suffix S(n) such that + * S(n) <= n < S(n+1), where S(n) = 2^(n*10) | 10^(3*n) + * (i.e. 1024/1000, 1,048,576/1,000,000, etc). Stop once S(n) + * is the largest prefix supported (i.e. don't bother computing + * and checking S(n+1). Since INDEX_MAX should be the largest + * suffix that fits (currently an exabyte), S(INDEX_MAX + 1) is + * never checked as it would overflow. + */ + while (index < INDEX_MAX) { + uint64_t newdiv = divisor * divamt; + + /* CTASSERT() guarantee these never trip */ + VERIFY3U(newdiv, >=, divamt); + VERIFY3U(newdiv, >=, divisor); + + if (n < newdiv) + break; + + divisor = newdiv; + index++; + } + + u = " KMGTPE"[index]; + + if (index == 0) { + rc = snprintf(buf, buflen, "%llu", n); + } else if (n % divisor == 0) { + /* + * If this is an even multiple of the base, always display + * without any decimal precision. + */ + rc = snprintf(buf, buflen, "%llu%c", n / divisor, u); + } else { + /* + * We want to choose a precision that reflects the best choice + * for fitting in 5 characters. This can get rather tricky + * when we have numbers that are very close to an order of + * magnitude. For example, when displaying 10239 (which is + * really 9.999K), we want only a single place of precision + * for 10.0K. We could develop some complex heuristics for + * this, but it's much easier just to try each combination + * in turn. + */ + int i; + for (i = 2; i >= 0; i--) { + if ((rc = snprintf(buf, buflen, "%.*f%c", i, + (double)n / divisor, u)) <= 5) + break; + } + } + + if (rc + 1 > buflen || rc < 0) + goto overflow; + + return; + +overflow: + /* prefer a more verbose message if possible */ + if (buflen > 10) + (void) strlcpy(buf, "<overflow>", buflen); + else + (void) strlcpy(buf, "??", buflen); +} + +void +nicenum(uint64_t num, char *buf, size_t buflen) +{ + nicenum_scale(num, 1, buf, buflen, 0); +} diff --git a/usr/src/lib/libcmdutils/libcmdutils.h b/usr/src/lib/libcmdutils/libcmdutils.h index 933ceb0fde..5f9957b861 100644 --- a/usr/src/lib/libcmdutils/libcmdutils.h +++ b/usr/src/lib/libcmdutils/libcmdutils.h @@ -216,6 +216,14 @@ extern void custr_reset(custr_t *); */ extern const char *custr_cstr(custr_t *str); +#define NN_DIVISOR_1000 (1U << 0) + +/* Minimum size for the output of nicenum, including NULL */ +#define NN_NUMBUF_SZ (6) + +void nicenum(uint64_t, char *, size_t); +void nicenum_scale(uint64_t, size_t, char *, size_t, uint32_t); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com index 31619b01c1..6eaf4332f2 100644 --- a/usr/src/lib/libzfs/Makefile.com +++ b/usr/src/lib/libzfs/Makefile.com @@ -70,7 +70,7 @@ INCS += -I../../libc/inc C99MODE= -xc99=%all C99LMODE= -Xc99=%all LDLIBS += -lc -lm -ldevid -lgen -lnvpair -luutil -lavl -lefi \ - -ladm -lidmap -ltsol -lmd -lumem -lzfs_core + -ladm -lidmap -ltsol -lmd -lumem -lzfs_core -lcmdutils CPPFLAGS += $(INCS) -D_LARGEFILE64_SOURCE=1 -D_REENTRANT $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c index 95b74604db..7442df57aa 100644 --- a/usr/src/lib/libzfs/common/libzfs_util.c +++ b/usr/src/lib/libzfs/common/libzfs_util.c @@ -45,6 +45,7 @@ #include <sys/mnttab.h> #include <sys/mntent.h> #include <sys/types.h> +#include <libcmdutils.h> #include <libzfs.h> #include <libzfs_core.h> @@ -575,42 +576,7 @@ zfs_strdup(libzfs_handle_t *hdl, const char *str) void zfs_nicenum(uint64_t num, char *buf, size_t buflen) { - uint64_t n = num; - int index = 0; - char u; - - while (n >= 1024) { - n /= 1024; - index++; - } - - u = " KMGTPE"[index]; - - if (index == 0) { - (void) snprintf(buf, buflen, "%llu", n); - } else if ((num & ((1ULL << 10 * index) - 1)) == 0) { - /* - * If this is an even multiple of the base, always display - * without any decimal precision. - */ - (void) snprintf(buf, buflen, "%llu%c", n, u); - } else { - /* - * We want to choose a precision that reflects the best choice - * for fitting in 5 characters. This can get rather tricky when - * we have numbers that are very close to an order of magnitude. - * For example, when displaying 10239 (which is really 9.999K), - * we want only a single place of precision for 10.0K. We could - * develop some complex heuristics for this, but it's much - * easier just to try each combination in turn. - */ - int i; - for (i = 2; i >= 0; i--) { - if (snprintf(buf, buflen, "%.*f%c", i, - (double)num / (1ULL << 10 * index), u) <= 5) - break; - } - } + nicenum(num, buf, buflen); } void diff --git a/usr/src/lib/libzpool/common/llib-lzpool b/usr/src/lib/libzpool/common/llib-lzpool index 3636b4e76e..27d01fd54d 100644 --- a/usr/src/lib/libzpool/common/llib-lzpool +++ b/usr/src/lib/libzpool/common/llib-lzpool @@ -62,6 +62,7 @@ #include <sys/dsl_userhold.h> #include <sys/blkptr.h> #include <sys/abd.h> +#include <libcmdutils.h> extern uint64_t metaslab_gang_bang; extern uint64_t metaslab_df_alloc_threshold; diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h index bc43d455e0..301ffc0df8 100644 --- a/usr/src/lib/libzpool/common/sys/zfs_context.h +++ b/usr/src/lib/libzpool/common/sys/zfs_context.h @@ -543,7 +543,7 @@ extern void kernel_init(int); extern void kernel_fini(void); struct spa; -extern void nicenum(uint64_t num, char *buf); +extern void nicenum(uint64_t num, char *buf, size_t); extern void show_pool_stats(struct spa *); extern int set_global_var(char *arg); diff --git a/usr/src/lib/libzpool/common/util.c b/usr/src/lib/libzpool/common/util.c index e929aa5645..363940677c 100644 --- a/usr/src/lib/libzpool/common/util.c +++ b/usr/src/lib/libzpool/common/util.c @@ -38,33 +38,6 @@ * Routines needed by more than one client of libzpool. */ -void -nicenum(uint64_t num, char *buf) -{ - uint64_t n = num; - int index = 0; - char u; - - while (n >= 1024) { - n = (n + (1024 / 2)) / 1024; /* Round up or down */ - index++; - } - - u = " KMGTPE"[index]; - - if (index == 0) { - (void) sprintf(buf, "%llu", (u_longlong_t)n); - } else if (n < 10 && (num & (num - 1)) != 0) { - (void) sprintf(buf, "%.2f%c", - (double)num / (1ULL << 10 * index), u); - } else if (n < 100 && (num & (num - 1)) != 0) { - (void) sprintf(buf, "%.1f%c", - (double)num / (1ULL << 10 * index), u); - } else { - (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u); - } -} - static void show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent) { @@ -97,15 +70,17 @@ show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent) sec = MAX(1, vs->vs_timestamp / NANOSEC); - nicenum(vs->vs_alloc, used); - nicenum(vs->vs_space - vs->vs_alloc, avail); - nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops); - nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops); - nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes); - nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes); - nicenum(vs->vs_read_errors, rerr); - nicenum(vs->vs_write_errors, werr); - nicenum(vs->vs_checksum_errors, cerr); + nicenum(vs->vs_alloc, used, sizeof (used)); + nicenum(vs->vs_space - vs->vs_alloc, avail, sizeof (avail)); + nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops, sizeof (rops)); + nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops, sizeof (wops)); + nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes, + sizeof (rbytes)); + nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes, + sizeof (wbytes)); + nicenum(vs->vs_read_errors, rerr, sizeof (rerr)); + nicenum(vs->vs_write_errors, werr, sizeof (werr)); + nicenum(vs->vs_checksum_errors, cerr, sizeof (cerr)); (void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n", indent, "", diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c index 28a5f106dd..c2c7162f53 100644 --- a/usr/src/uts/common/fs/zfs/dmu.c +++ b/usr/src/uts/common/fs/zfs/dmu.c @@ -1645,6 +1645,13 @@ dmu_sync_late_arrival(zio_t *pio, objset_t *os, dmu_sync_cb_t *done, zgd_t *zgd, return (SET_ERROR(EIO)); } + /* + * In order to prevent the zgd's lwb from being free'd prior to + * dmu_sync_late_arrival_done() being called, we have to ensure + * the lwb's "max txg" takes this tx's txg into account. + */ + zil_lwb_add_txg(zgd->zgd_lwb, dmu_tx_get_txg(tx)); + dsa = kmem_alloc(sizeof (dmu_sync_arg_t), KM_SLEEP); dsa->dsa_dr = NULL; dsa->dsa_done = done; diff --git a/usr/src/uts/common/fs/zfs/dsl_destroy.c b/usr/src/uts/common/fs/zfs/dsl_destroy.c index b632070e39..133b47108c 100644 --- a/usr/src/uts/common/fs/zfs/dsl_destroy.c +++ b/usr/src/uts/common/fs/zfs/dsl_destroy.c @@ -501,6 +501,7 @@ dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer, nvpair_name(pair), B_TRUE); } fnvlist_add_nvlist(arg, "snaps", snaps_normalized); + fnvlist_free(snaps_normalized); fnvlist_add_boolean_value(arg, "defer", defer); nvlist_t *wrapper = fnvlist_alloc(); diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h index 1dba052ae0..7cb65596e4 100644 --- a/usr/src/uts/common/fs/zfs/sys/dmu.h +++ b/usr/src/uts/common/fs/zfs/sys/dmu.h @@ -911,7 +911,7 @@ uint64_t dmu_tx_get_txg(dmu_tx_t *tx); * {zfs,zvol,ztest}_get_done() args */ typedef struct zgd { - struct zilog *zgd_zilog; + struct lwb *zgd_lwb; struct blkptr *zgd_bp; dmu_buf_t *zgd_db; struct rl *zgd_rl; diff --git a/usr/src/uts/common/fs/zfs/sys/zil.h b/usr/src/uts/common/fs/zfs/sys/zil.h index 23ef83dfe4..2b0484ad5e 100644 --- a/usr/src/uts/common/fs/zfs/sys/zil.h +++ b/usr/src/uts/common/fs/zfs/sys/zil.h @@ -40,6 +40,7 @@ extern "C" { struct dsl_pool; struct dsl_dataset; +struct lwb; /* * Intent log format: @@ -140,6 +141,7 @@ typedef enum zil_create { /* * Intent log transaction types and record structures */ +#define TX_COMMIT 0 /* Commit marker (no on-disk state) */ #define TX_CREATE 1 /* Create file */ #define TX_MKDIR 2 /* Make directory */ #define TX_MKXATTR 3 /* Make XATTR directory */ @@ -388,7 +390,8 @@ typedef int zil_parse_blk_func_t(zilog_t *zilog, blkptr_t *bp, void *arg, typedef int zil_parse_lr_func_t(zilog_t *zilog, lr_t *lr, void *arg, uint64_t txg); typedef int zil_replay_func_t(); -typedef int zil_get_data_t(void *arg, lr_write_t *lr, char *dbuf, zio_t *zio); +typedef int zil_get_data_t(void *arg, lr_write_t *lr, char *dbuf, + struct lwb *lwb, zio_t *zio); extern int zil_parse(zilog_t *zilog, zil_parse_blk_func_t *parse_blk_func, zil_parse_lr_func_t *parse_lr_func, void *arg, uint64_t txg); @@ -426,7 +429,8 @@ extern void zil_clean(zilog_t *zilog, uint64_t synced_txg); extern int zil_suspend(const char *osname, void **cookiep); extern void zil_resume(void *cookie); -extern void zil_add_block(zilog_t *zilog, const blkptr_t *bp); +extern void zil_lwb_add_block(struct lwb *lwb, const blkptr_t *bp); +extern void zil_lwb_add_txg(struct lwb *lwb, uint64_t txg); extern int zil_bp_tree_add(zilog_t *zilog, const blkptr_t *bp); extern void zil_set_sync(zilog_t *zilog, uint64_t syncval); diff --git a/usr/src/uts/common/fs/zfs/sys/zil_impl.h b/usr/src/uts/common/fs/zfs/sys/zil_impl.h index 0618133124..308f4a5da3 100644 --- a/usr/src/uts/common/fs/zfs/sys/zil_impl.h +++ b/usr/src/uts/common/fs/zfs/sys/zil_impl.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2017 by Delphix. All rights reserved. * Copyright (c) 2014 Integros [integros.com] */ @@ -37,7 +37,30 @@ extern "C" { #endif /* - * Log write buffer. + * Possbile states for a given lwb structure. An lwb will start out in + * the "closed" state, and then transition to the "opened" state via a + * call to zil_lwb_write_open(). After the lwb is "open", it can + * transition into the "issued" state via zil_lwb_write_issue(). After + * the lwb's zio completes, and the vdev's are flushed, the lwb will + * transition into the "done" state via zil_lwb_write_done(), and the + * structure eventually freed. + */ +typedef enum { + LWB_STATE_CLOSED, + LWB_STATE_OPENED, + LWB_STATE_ISSUED, + LWB_STATE_DONE, + LWB_NUM_STATES +} lwb_state_t; + +/* + * Log write block (lwb) + * + * Prior to an lwb being issued to disk via zil_lwb_write_issue(), it + * will be protected by the zilog's "zl_writer_lock". Basically, prior + * to it being issued, it will only be accessed by the thread that's + * holding the "zl_writer_lock". After the lwb is issued, the zilog's + * "zl_lock" is used to protect the lwb against concurrent access. */ typedef struct lwb { zilog_t *lwb_zilog; /* back pointer to log struct */ @@ -45,14 +68,45 @@ typedef struct lwb { boolean_t lwb_slog; /* lwb_blk is on SLOG device */ int lwb_nused; /* # used bytes in buffer */ int lwb_sz; /* size of block and buffer */ + lwb_state_t lwb_state; /* the state of this lwb */ char *lwb_buf; /* log write buffer */ - zio_t *lwb_zio; /* zio for this buffer */ + zio_t *lwb_write_zio; /* zio for the lwb buffer */ + zio_t *lwb_root_zio; /* root zio for lwb write and flushes */ dmu_tx_t *lwb_tx; /* tx for log block allocation */ uint64_t lwb_max_txg; /* highest txg in this lwb */ list_node_t lwb_node; /* zilog->zl_lwb_list linkage */ + list_t lwb_waiters; /* list of zil_commit_waiter's */ + avl_tree_t lwb_vdev_tree; /* vdevs to flush after lwb write */ + kmutex_t lwb_vdev_lock; /* protects lwb_vdev_tree */ + hrtime_t lwb_issued_timestamp; /* when was the lwb issued? */ } lwb_t; /* + * ZIL commit waiter. + * + * This structure is allocated each time zil_commit() is called, and is + * used by zil_commit() to communicate with other parts of the ZIL, such + * that zil_commit() can know when it safe for it return. For more + * details, see the comment above zil_commit(). + * + * The "zcw_lock" field is used to protect the commit waiter against + * concurrent access. This lock is often acquired while already holding + * the zilog's "zl_writer_lock" or "zl_lock"; see the functions + * zil_process_commit_list() and zil_lwb_flush_vdevs_done() as examples + * of this. Thus, one must be careful not to acquire the + * "zl_writer_lock" or "zl_lock" when already holding the "zcw_lock"; + * e.g. see the zil_commit_waiter_timeout() function. + */ +typedef struct zil_commit_waiter { + kcondvar_t zcw_cv; /* signalled when "done" */ + kmutex_t zcw_lock; /* protects fields of this struct */ + list_node_t zcw_node; /* linkage in lwb_t:lwb_waiter list */ + lwb_t *zcw_lwb; /* back pointer to lwb when linked */ + boolean_t zcw_done; /* B_TRUE when "done", else B_FALSE */ + int zcw_zio_error; /* contains the zio io_error value */ +} zil_commit_waiter_t; + +/* * Intent log transaction lists */ typedef struct itxs { @@ -94,20 +148,20 @@ struct zilog { const zil_header_t *zl_header; /* log header buffer */ objset_t *zl_os; /* object set we're logging */ zil_get_data_t *zl_get_data; /* callback to get object content */ - zio_t *zl_root_zio; /* log writer root zio */ + lwb_t *zl_last_lwb_opened; /* most recent lwb opened */ + hrtime_t zl_last_lwb_latency; /* zio latency of last lwb done */ uint64_t zl_lr_seq; /* on-disk log record sequence number */ uint64_t zl_commit_lr_seq; /* last committed on-disk lr seq */ uint64_t zl_destroy_txg; /* txg of last zil_destroy() */ uint64_t zl_replayed_seq[TXG_SIZE]; /* last replayed rec seq */ uint64_t zl_replaying_seq; /* current replay seq number */ uint32_t zl_suspend; /* log suspend count */ - kcondvar_t zl_cv_writer; /* log writer thread completion */ kcondvar_t zl_cv_suspend; /* log suspend completion */ uint8_t zl_suspending; /* log is currently suspending */ uint8_t zl_keep_first; /* keep first log block in destroy */ uint8_t zl_replay; /* replaying records while set */ uint8_t zl_stop_sync; /* for debugging */ - uint8_t zl_writer; /* boolean: write setup in progress */ + kmutex_t zl_writer_lock; /* single writer, per ZIL, at a time */ uint8_t zl_logbias; /* latency or throughput */ uint8_t zl_sync; /* synchronous or asynchronous */ int zl_parse_error; /* last zil_parse() error */ @@ -115,15 +169,10 @@ struct zilog { uint64_t zl_parse_lr_seq; /* highest lr seq on last parse */ uint64_t zl_parse_blk_count; /* number of blocks parsed */ uint64_t zl_parse_lr_count; /* number of log records parsed */ - uint64_t zl_next_batch; /* next batch number */ - uint64_t zl_com_batch; /* committed batch number */ - kcondvar_t zl_cv_batch[2]; /* batch condition variables */ itxg_t zl_itxg[TXG_SIZE]; /* intent log txg chains */ list_t zl_itx_commit_list; /* itx list to be committed */ uint64_t zl_cur_used; /* current commit log size used */ list_t zl_lwb_list; /* in-flight log write list */ - kmutex_t zl_vdev_lock; /* protects zl_vdev_tree */ - avl_tree_t zl_vdev_tree; /* vdevs to flush in zil_commit() */ avl_tree_t zl_bp_tree; /* track bps during log parse */ clock_t zl_replay_time; /* lbolt of when replay started */ uint64_t zl_replay_blks; /* number of log blocks replayed */ @@ -131,6 +180,7 @@ struct zilog { uint_t zl_prev_blks[ZIL_PREV_BLKS]; /* size - sector rounded */ uint_t zl_prev_rotor; /* rotor for zl_prev[] */ txg_node_t zl_dirty_link; /* protected by dp_dirty_zilogs list */ + uint64_t zl_dirty_max_txg; /* highest txg used to dirty zilog */ }; typedef struct zil_bp_node { diff --git a/usr/src/uts/common/fs/zfs/sys/zio.h b/usr/src/uts/common/fs/zfs/sys/zio.h index 9c2beafaa6..281b2a678d 100644 --- a/usr/src/uts/common/fs/zfs/sys/zio.h +++ b/usr/src/uts/common/fs/zfs/sys/zio.h @@ -558,6 +558,7 @@ extern enum zio_checksum zio_checksum_dedup_select(spa_t *spa, extern enum zio_compress zio_compress_select(spa_t *spa, enum zio_compress child, enum zio_compress parent); +extern void zio_cancel(zio_t *zio); extern void zio_suspend(spa_t *spa, zio_t *zio); extern int zio_resume(spa_t *spa); extern void zio_resume_wait(spa_t *spa); diff --git a/usr/src/uts/common/fs/zfs/txg.c b/usr/src/uts/common/fs/zfs/txg.c index a3ad0fb1aa..52740996b1 100644 --- a/usr/src/uts/common/fs/zfs/txg.c +++ b/usr/src/uts/common/fs/zfs/txg.c @@ -159,7 +159,7 @@ txg_fini(dsl_pool_t *dp) tx_state_t *tx = &dp->dp_tx; int c; - ASSERT(tx->tx_threads == 0); + ASSERT0(tx->tx_threads); mutex_destroy(&tx->tx_sync_lock); @@ -200,7 +200,7 @@ txg_sync_start(dsl_pool_t *dp) dprintf("pool %p\n", dp); - ASSERT(tx->tx_threads == 0); + ASSERT0(tx->tx_threads); tx->tx_threads = 2; @@ -262,7 +262,7 @@ txg_sync_stop(dsl_pool_t *dp) /* * Finish off any work in progress. */ - ASSERT(tx->tx_threads == 2); + ASSERT3U(tx->tx_threads, ==, 2); /* * We need to ensure that we've vacated the deferred space_maps. @@ -274,7 +274,7 @@ txg_sync_stop(dsl_pool_t *dp) */ mutex_enter(&tx->tx_sync_lock); - ASSERT(tx->tx_threads == 2); + ASSERT3U(tx->tx_threads, ==, 2); tx->tx_exiting = 1; @@ -611,7 +611,7 @@ txg_wait_synced(dsl_pool_t *dp, uint64_t txg) ASSERT(!dsl_pool_config_held(dp)); mutex_enter(&tx->tx_sync_lock); - ASSERT(tx->tx_threads == 2); + ASSERT3U(tx->tx_threads, ==, 2); if (txg == 0) txg = tx->tx_open_txg + TXG_DEFER_SIZE; if (tx->tx_sync_txg_waiting < txg) @@ -636,7 +636,7 @@ txg_wait_open(dsl_pool_t *dp, uint64_t txg) ASSERT(!dsl_pool_config_held(dp)); mutex_enter(&tx->tx_sync_lock); - ASSERT(tx->tx_threads == 2); + ASSERT3U(tx->tx_threads, ==, 2); if (txg == 0) txg = tx->tx_open_txg + 1; if (tx->tx_quiesce_txg_waiting < txg) diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c index 7cb53007ed..b7bf82b43a 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vnops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c @@ -83,6 +83,7 @@ #include <sys/kidmap.h> #include <sys/cred.h> #include <sys/attr.h> +#include <sys/zil.h> /* * Programming rules. @@ -1034,7 +1035,7 @@ zfs_get_done(zgd_t *zgd, int error) VN_RELE_ASYNC(ZTOV(zp), dsl_pool_vnrele_taskq(dmu_objset_pool(os))); if (error == 0 && zgd->zgd_bp) - zil_add_block(zgd->zgd_zilog, zgd->zgd_bp); + zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp); kmem_free(zgd, sizeof (zgd_t)); } @@ -1047,7 +1048,7 @@ static int zil_fault_io = 0; * Get data to generate a TX_WRITE intent log record. */ int -zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) +zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio) { zfsvfs_t *zfsvfs = arg; objset_t *os = zfsvfs->z_os; @@ -1059,8 +1060,9 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) zgd_t *zgd; int error = 0; - ASSERT(zio != NULL); - ASSERT(size != 0); + ASSERT3P(lwb, !=, NULL); + ASSERT3P(zio, !=, NULL); + ASSERT3U(size, !=, 0); /* * Nothing to do if the file has been removed @@ -1078,7 +1080,7 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) } zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_SLEEP); - zgd->zgd_zilog = zfsvfs->z_log; + zgd->zgd_lwb = lwb; zgd->zgd_private = zp; /* diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c index 1e1f1cdd5d..8ad62309eb 100644 --- a/usr/src/uts/common/fs/zfs/zil.c +++ b/usr/src/uts/common/fs/zfs/zil.c @@ -43,30 +43,51 @@ #include <sys/abd.h> /* - * The zfs intent log (ZIL) saves transaction records of system calls - * that change the file system in memory with enough information - * to be able to replay them. These are stored in memory until - * either the DMU transaction group (txg) commits them to the stable pool - * and they can be discarded, or they are flushed to the stable log - * (also in the pool) due to a fsync, O_DSYNC or other synchronous - * requirement. In the event of a panic or power fail then those log - * records (transactions) are replayed. + * The ZFS Intent Log (ZIL) saves "transaction records" (itxs) of system + * calls that change the file system. Each itx has enough information to + * be able to replay them after a system crash, power loss, or + * equivalent failure mode. These are stored in memory until either: * - * There is one ZIL per file system. Its on-disk (pool) format consists - * of 3 parts: + * 1. they are committed to the pool by the DMU transaction group + * (txg), at which point they can be discarded; or + * 2. they are committed to the on-disk ZIL for the dataset being + * modified (e.g. due to an fsync, O_DSYNC, or other synchronous + * requirement). * - * - ZIL header - * - ZIL blocks - * - ZIL records + * In the event of a crash or power loss, the itxs contained by each + * dataset's on-disk ZIL will be replayed when that dataset is first + * instantianted (e.g. if the dataset is a normal fileystem, when it is + * first mounted). * - * A log record holds a system call transaction. Log blocks can - * hold many log records and the blocks are chained together. - * Each ZIL block contains a block pointer (blkptr_t) to the next - * ZIL block in the chain. The ZIL header points to the first - * block in the chain. Note there is not a fixed place in the pool - * to hold blocks. They are dynamically allocated and freed as - * needed from the blocks available. Figure X shows the ZIL structure: + * As hinted at above, there is one ZIL per dataset (both the in-memory + * representation, and the on-disk representation). The on-disk format + * consists of 3 parts: + * + * - a single, per-dataset, ZIL header; which points to a chain of + * - zero or more ZIL blocks; each of which contains + * - zero or more ZIL records + * + * A ZIL record holds the information necessary to replay a single + * system call transaction. A ZIL block can hold many ZIL records, and + * the blocks are chained together, similarly to a singly linked list. + * + * Each ZIL block contains a block pointer (blkptr_t) to the next ZIL + * block in the chain, and the ZIL header points to the first block in + * the chain. + * + * Note, there is not a fixed place in the pool to hold these ZIL + * blocks; they are dynamically allocated and freed as needed from the + * blocks available on the pool, though they can be preferentially + * allocated from a dedicated "log" vdev. + */ + +/* + * This controls the amount of time that a ZIL block (lwb) will remain + * "open" when it isn't "full", and it has a thread waiting for it to be + * committed to stable storage. Please refer to the zil_commit_waiter() + * function (and the comments within it) for more details. */ +int zfs_commit_timeout_pct = 5; /* * Disable intent logging replay. This global ZIL switch affects all pools. @@ -88,6 +109,7 @@ boolean_t zfs_nocacheflush = B_FALSE; uint64_t zil_slog_bulk = 768 * 1024; static kmem_cache_t *zil_lwb_cache; +static kmem_cache_t *zil_zcw_cache; static void zil_async_to_sync(zilog_t *zilog, uint64_t foid); @@ -437,6 +459,20 @@ zil_free_log_record(zilog_t *zilog, lr_t *lrc, void *tx, uint64_t claim_txg) return (0); } +static int +zil_lwb_vdev_compare(const void *x1, const void *x2) +{ + const uint64_t v1 = ((zil_vdev_node_t *)x1)->zv_vdev; + const uint64_t v2 = ((zil_vdev_node_t *)x2)->zv_vdev; + + if (v1 < v2) + return (-1); + if (v1 > v2) + return (1); + + return (0); +} + static lwb_t * zil_alloc_lwb(zilog_t *zilog, blkptr_t *bp, boolean_t slog, uint64_t txg) { @@ -446,10 +482,13 @@ zil_alloc_lwb(zilog_t *zilog, blkptr_t *bp, boolean_t slog, uint64_t txg) lwb->lwb_zilog = zilog; lwb->lwb_blk = *bp; lwb->lwb_slog = slog; + lwb->lwb_state = LWB_STATE_CLOSED; lwb->lwb_buf = zio_buf_alloc(BP_GET_LSIZE(bp)); lwb->lwb_max_txg = txg; - lwb->lwb_zio = NULL; + lwb->lwb_write_zio = NULL; + lwb->lwb_root_zio = NULL; lwb->lwb_tx = NULL; + lwb->lwb_issued_timestamp = 0; if (BP_GET_CHECKSUM(bp) == ZIO_CHECKSUM_ZILOG2) { lwb->lwb_nused = sizeof (zil_chain_t); lwb->lwb_sz = BP_GET_LSIZE(bp); @@ -462,9 +501,54 @@ zil_alloc_lwb(zilog_t *zilog, blkptr_t *bp, boolean_t slog, uint64_t txg) list_insert_tail(&zilog->zl_lwb_list, lwb); mutex_exit(&zilog->zl_lock); + ASSERT(!MUTEX_HELD(&lwb->lwb_vdev_lock)); + ASSERT(avl_is_empty(&lwb->lwb_vdev_tree)); + ASSERT(list_is_empty(&lwb->lwb_waiters)); + return (lwb); } +static void +zil_free_lwb(zilog_t *zilog, lwb_t *lwb) +{ + ASSERT(MUTEX_HELD(&zilog->zl_lock)); + ASSERT(!MUTEX_HELD(&lwb->lwb_vdev_lock)); + ASSERT(list_is_empty(&lwb->lwb_waiters)); + + if (lwb->lwb_state == LWB_STATE_OPENED) { + avl_tree_t *t = &lwb->lwb_vdev_tree; + void *cookie = NULL; + zil_vdev_node_t *zv; + + while ((zv = avl_destroy_nodes(t, &cookie)) != NULL) + kmem_free(zv, sizeof (*zv)); + + ASSERT3P(lwb->lwb_root_zio, !=, NULL); + ASSERT3P(lwb->lwb_write_zio, !=, NULL); + + zio_cancel(lwb->lwb_root_zio); + zio_cancel(lwb->lwb_write_zio); + + lwb->lwb_root_zio = NULL; + lwb->lwb_write_zio = NULL; + } else { + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_ISSUED); + } + + ASSERT(avl_is_empty(&lwb->lwb_vdev_tree)); + ASSERT3P(lwb->lwb_write_zio, ==, NULL); + ASSERT3P(lwb->lwb_root_zio, ==, NULL); + + /* + * Clear the zilog's field to indicate this lwb is no longer + * valid, and prevent use-after-free errors. + */ + if (zilog->zl_last_lwb_opened == lwb) + zilog->zl_last_lwb_opened = NULL; + + kmem_cache_free(zil_lwb_cache, lwb); +} + /* * Called when we create in-memory log transactions so that we know * to cleanup the itxs at the end of spa_sync(). @@ -475,12 +559,16 @@ zilog_dirty(zilog_t *zilog, uint64_t txg) dsl_pool_t *dp = zilog->zl_dmu_pool; dsl_dataset_t *ds = dmu_objset_ds(zilog->zl_os); + ASSERT(spa_writeable(zilog->zl_spa)); + if (ds->ds_is_snapshot) panic("dirtying snapshot!"); if (txg_list_add(&dp->dp_dirty_zilogs, zilog, txg)) { /* up the hold count until we can be written out */ dmu_buf_add_ref(ds->ds_dbuf, zilog); + + zilog->zl_dirty_max_txg = MAX(txg, zilog->zl_dirty_max_txg); } } @@ -548,7 +636,7 @@ zil_create(zilog_t *zilog) */ if (BP_IS_HOLE(&blk) || BP_SHOULD_BYTESWAP(&blk)) { tx = dmu_tx_create(zilog->zl_os); - VERIFY(dmu_tx_assign(tx, TXG_WAIT) == 0); + VERIFY0(dmu_tx_assign(tx, TXG_WAIT)); dsl_dataset_dirty(dmu_objset_ds(zilog->zl_os), tx); txg = dmu_tx_get_txg(tx); @@ -565,7 +653,7 @@ zil_create(zilog_t *zilog) } /* - * Allocate a log write buffer (lwb) for the first log block. + * Allocate a log write block (lwb) for the first log block. */ if (error == 0) lwb = zil_alloc_lwb(zilog, &blk, slog, txg); @@ -586,13 +674,13 @@ zil_create(zilog_t *zilog) } /* - * In one tx, free all log blocks and clear the log header. - * If keep_first is set, then we're replaying a log with no content. - * We want to keep the first block, however, so that the first - * synchronous transaction doesn't require a txg_wait_synced() - * in zil_create(). We don't need to txg_wait_synced() here either - * when keep_first is set, because both zil_create() and zil_destroy() - * will wait for any in-progress destroys to complete. + * In one tx, free all log blocks and clear the log header. If keep_first + * is set, then we're replaying a log with no content. We want to keep the + * first block, however, so that the first synchronous transaction doesn't + * require a txg_wait_synced() in zil_create(). We don't need to + * txg_wait_synced() here either when keep_first is set, because both + * zil_create() and zil_destroy() will wait for any in-progress destroys + * to complete. */ void zil_destroy(zilog_t *zilog, boolean_t keep_first) @@ -613,7 +701,7 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first) return; tx = dmu_tx_create(zilog->zl_os); - VERIFY(dmu_tx_assign(tx, TXG_WAIT) == 0); + VERIFY0(dmu_tx_assign(tx, TXG_WAIT)); dsl_dataset_dirty(dmu_objset_ds(zilog->zl_os), tx); txg = dmu_tx_get_txg(tx); @@ -630,8 +718,8 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first) list_remove(&zilog->zl_lwb_list, lwb); if (lwb->lwb_buf != NULL) zio_buf_free(lwb->lwb_buf, lwb->lwb_sz); - zio_free_zil(zilog->zl_spa, txg, &lwb->lwb_blk); - kmem_cache_free(zil_lwb_cache, lwb); + zio_free(zilog->zl_spa, txg, &lwb->lwb_blk); + zil_free_lwb(zilog, lwb); } } else if (!keep_first) { zil_destroy_sync(zilog, tx); @@ -769,24 +857,64 @@ zil_check_log_chain(dsl_pool_t *dp, dsl_dataset_t *ds, void *tx) return ((error == ECKSUM || error == ENOENT) ? 0 : error); } -static int -zil_vdev_compare(const void *x1, const void *x2) +/* + * When an itx is "skipped", this function is used to properly mark the + * waiter as "done, and signal any thread(s) waiting on it. An itx can + * be skipped (and not committed to an lwb) for a variety of reasons, + * one of them being that the itx was committed via spa_sync(), prior to + * it being committed to an lwb; this can happen if a thread calling + * zil_commit() is racing with spa_sync(). + */ +static void +zil_commit_waiter_skip(zil_commit_waiter_t *zcw) { - const uint64_t v1 = ((zil_vdev_node_t *)x1)->zv_vdev; - const uint64_t v2 = ((zil_vdev_node_t *)x2)->zv_vdev; + mutex_enter(&zcw->zcw_lock); + ASSERT3B(zcw->zcw_done, ==, B_FALSE); + zcw->zcw_done = B_TRUE; + cv_broadcast(&zcw->zcw_cv); + mutex_exit(&zcw->zcw_lock); +} - if (v1 < v2) - return (-1); - if (v1 > v2) - return (1); +/* + * This function is used when the given waiter is to be linked into an + * lwb's "lwb_waiter" list; i.e. when the itx is committed to the lwb. + * At this point, the waiter will no longer be referenced by the itx, + * and instead, will be referenced by the lwb. + */ +static void +zil_commit_waiter_link_lwb(zil_commit_waiter_t *zcw, lwb_t *lwb) +{ + mutex_enter(&zcw->zcw_lock); + ASSERT(!list_link_active(&zcw->zcw_node)); + ASSERT3P(zcw->zcw_lwb, ==, NULL); + ASSERT3P(lwb, !=, NULL); + ASSERT(lwb->lwb_state == LWB_STATE_OPENED || + lwb->lwb_state == LWB_STATE_ISSUED); + + list_insert_tail(&lwb->lwb_waiters, zcw); + zcw->zcw_lwb = lwb; + mutex_exit(&zcw->zcw_lock); +} - return (0); +/* + * This function is used when zio_alloc_zil() fails to allocate a ZIL + * block, and the given waiter must be linked to the "nolwb waiters" + * list inside of zil_process_commit_list(). + */ +static void +zil_commit_waiter_link_nolwb(zil_commit_waiter_t *zcw, list_t *nolwb) +{ + mutex_enter(&zcw->zcw_lock); + ASSERT(!list_link_active(&zcw->zcw_node)); + ASSERT3P(zcw->zcw_lwb, ==, NULL); + list_insert_tail(nolwb, zcw); + mutex_exit(&zcw->zcw_lock); } void -zil_add_block(zilog_t *zilog, const blkptr_t *bp) +zil_lwb_add_block(lwb_t *lwb, const blkptr_t *bp) { - avl_tree_t *t = &zilog->zl_vdev_tree; + avl_tree_t *t = &lwb->lwb_vdev_tree; avl_index_t where; zil_vdev_node_t *zv, zvsearch; int ndvas = BP_GET_NDVAS(bp); @@ -795,14 +923,7 @@ zil_add_block(zilog_t *zilog, const blkptr_t *bp) if (zfs_nocacheflush) return; - ASSERT(zilog->zl_writer); - - /* - * Even though we're zl_writer, we still need a lock because the - * zl_get_data() callbacks may have dmu_sync() done callbacks - * that will run concurrently. - */ - mutex_enter(&zilog->zl_vdev_lock); + mutex_enter(&lwb->lwb_vdev_lock); for (i = 0; i < ndvas; i++) { zvsearch.zv_vdev = DVA_GET_VDEV(&bp->blk_dva[i]); if (avl_find(t, &zvsearch, &where) == NULL) { @@ -811,56 +932,116 @@ zil_add_block(zilog_t *zilog, const blkptr_t *bp) avl_insert(t, zv, where); } } - mutex_exit(&zilog->zl_vdev_lock); + mutex_exit(&lwb->lwb_vdev_lock); +} + +void +zil_lwb_add_txg(lwb_t *lwb, uint64_t txg) +{ + lwb->lwb_max_txg = MAX(lwb->lwb_max_txg, txg); } +/* + * This function is a called after all VDEVs associated with a given lwb + * write have completed their DKIOCFLUSHWRITECACHE command; or as soon + * as the lwb write completes, if "zfs_nocacheflush" is set. + * + * The intention is for this function to be called as soon as the + * contents of an lwb are considered "stable" on disk, and will survive + * any sudden loss of power. At this point, any threads waiting for the + * lwb to reach this state are signalled, and the "waiter" structures + * are marked "done". + */ static void -zil_flush_vdevs(zilog_t *zilog) +zil_lwb_flush_vdevs_done(zio_t *zio) { - spa_t *spa = zilog->zl_spa; - avl_tree_t *t = &zilog->zl_vdev_tree; - void *cookie = NULL; - zil_vdev_node_t *zv; - zio_t *zio; + lwb_t *lwb = zio->io_private; + zilog_t *zilog = lwb->lwb_zilog; + dmu_tx_t *tx = lwb->lwb_tx; + zil_commit_waiter_t *zcw; + + spa_config_exit(zilog->zl_spa, SCL_STATE, lwb); - ASSERT(zilog->zl_writer); + zio_buf_free(lwb->lwb_buf, lwb->lwb_sz); + + mutex_enter(&zilog->zl_lock); /* - * We don't need zl_vdev_lock here because we're the zl_writer, - * and all zl_get_data() callbacks are done. + * Ensure the lwb buffer pointer is cleared before releasing the + * txg. If we have had an allocation failure and the txg is + * waiting to sync then we want zil_sync() to remove the lwb so + * that it's not picked up as the next new one in + * zil_process_commit_list(). zil_sync() will only remove the + * lwb if lwb_buf is null. */ - if (avl_numnodes(t) == 0) - return; + lwb->lwb_buf = NULL; + lwb->lwb_tx = NULL; - spa_config_enter(spa, SCL_STATE, FTAG, RW_READER); + ASSERT3U(lwb->lwb_issued_timestamp, >, 0); + zilog->zl_last_lwb_latency = gethrtime() - lwb->lwb_issued_timestamp; - zio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL); + lwb->lwb_root_zio = NULL; + lwb->lwb_state = LWB_STATE_DONE; - while ((zv = avl_destroy_nodes(t, &cookie)) != NULL) { - vdev_t *vd = vdev_lookup_top(spa, zv->zv_vdev); - if (vd != NULL) - zio_flush(zio, vd); - kmem_free(zv, sizeof (*zv)); + if (zilog->zl_last_lwb_opened == lwb) { + /* + * Remember the highest committed log sequence number + * for ztest. We only update this value when all the log + * writes succeeded, because ztest wants to ASSERT that + * it got the whole log chain. + */ + zilog->zl_commit_lr_seq = zilog->zl_lr_seq; } + while ((zcw = list_head(&lwb->lwb_waiters)) != NULL) { + mutex_enter(&zcw->zcw_lock); + + ASSERT(list_link_active(&zcw->zcw_node)); + list_remove(&lwb->lwb_waiters, zcw); + + ASSERT3P(zcw->zcw_lwb, ==, lwb); + zcw->zcw_lwb = NULL; + + zcw->zcw_zio_error = zio->io_error; + + ASSERT3B(zcw->zcw_done, ==, B_FALSE); + zcw->zcw_done = B_TRUE; + cv_broadcast(&zcw->zcw_cv); + + mutex_exit(&zcw->zcw_lock); + } + + mutex_exit(&zilog->zl_lock); + /* - * Wait for all the flushes to complete. Not all devices actually - * support the DKIOCFLUSHWRITECACHE ioctl, so it's OK if it fails. + * Now that we've written this log block, we have a stable pointer + * to the next block in the chain, so it's OK to let the txg in + * which we allocated the next block sync. */ - (void) zio_wait(zio); - - spa_config_exit(spa, SCL_STATE, FTAG); + dmu_tx_commit(tx); } /* - * Function called when a log block write completes + * This is called when an lwb write completes. This means, this specific + * lwb was written to disk, and all dependent lwb have also been + * written to disk. + * + * At this point, a DKIOCFLUSHWRITECACHE command hasn't been issued to + * the VDEVs involved in writing out this specific lwb. The lwb will be + * "done" once zil_lwb_flush_vdevs_done() is called, which occurs in the + * zio completion callback for the lwb's root zio. */ static void zil_lwb_write_done(zio_t *zio) { lwb_t *lwb = zio->io_private; + spa_t *spa = zio->io_spa; zilog_t *zilog = lwb->lwb_zilog; - dmu_tx_t *tx = lwb->lwb_tx; + avl_tree_t *t = &lwb->lwb_vdev_tree; + void *cookie = NULL; + zil_vdev_node_t *zv; + + ASSERT3S(spa_config_held(spa, SCL_STATE, RW_READER), !=, 0); ASSERT(BP_GET_COMPRESS(zio->io_bp) == ZIO_COMPRESS_OFF); ASSERT(BP_GET_TYPE(zio->io_bp) == DMU_OT_INTENT_LOG); @@ -870,58 +1051,115 @@ zil_lwb_write_done(zio_t *zio) ASSERT(!BP_IS_HOLE(zio->io_bp)); ASSERT(BP_GET_FILL(zio->io_bp) == 0); - /* - * Ensure the lwb buffer pointer is cleared before releasing - * the txg. If we have had an allocation failure and - * the txg is waiting to sync then we want want zil_sync() - * to remove the lwb so that it's not picked up as the next new - * one in zil_commit_writer(). zil_sync() will only remove - * the lwb if lwb_buf is null. - */ abd_put(zio->io_abd); - zio_buf_free(lwb->lwb_buf, lwb->lwb_sz); + + ASSERT3S(lwb->lwb_state, ==, LWB_STATE_ISSUED); + mutex_enter(&zilog->zl_lock); - lwb->lwb_buf = NULL; - lwb->lwb_tx = NULL; + lwb->lwb_write_zio = NULL; mutex_exit(&zilog->zl_lock); + if (avl_numnodes(t) == 0) + return; + /* - * Now that we've written this log block, we have a stable pointer - * to the next block in the chain, so it's OK to let the txg in - * which we allocated the next block sync. + * If there was an IO error, we're not going to call zio_flush() + * on these vdevs, so we simply empty the tree and free the + * nodes. We avoid calling zio_flush() since there isn't any + * good reason for doing so, after the lwb block failed to be + * written out. */ - dmu_tx_commit(tx); + if (zio->io_error != 0) { + while ((zv = avl_destroy_nodes(t, &cookie)) != NULL) + kmem_free(zv, sizeof (*zv)); + return; + } + + while ((zv = avl_destroy_nodes(t, &cookie)) != NULL) { + vdev_t *vd = vdev_lookup_top(spa, zv->zv_vdev); + if (vd != NULL) + zio_flush(lwb->lwb_root_zio, vd); + kmem_free(zv, sizeof (*zv)); + } } /* - * Initialize the io for a log block. + * This function's purpose is to "open" an lwb such that it is ready to + * accept new itxs being committed to it. To do this, the lwb's zio + * structures are created, and linked to the lwb. This function is + * idempotent; if the passed in lwb has already been opened, this + * function is essentially a no-op. */ static void -zil_lwb_write_init(zilog_t *zilog, lwb_t *lwb) +zil_lwb_write_open(zilog_t *zilog, lwb_t *lwb) { zbookmark_phys_t zb; zio_priority_t prio; + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); + ASSERT3P(lwb, !=, NULL); + EQUIV(lwb->lwb_root_zio == NULL, lwb->lwb_state == LWB_STATE_CLOSED); + EQUIV(lwb->lwb_root_zio != NULL, lwb->lwb_state == LWB_STATE_OPENED); + SET_BOOKMARK(&zb, lwb->lwb_blk.blk_cksum.zc_word[ZIL_ZC_OBJSET], ZB_ZIL_OBJECT, ZB_ZIL_LEVEL, lwb->lwb_blk.blk_cksum.zc_word[ZIL_ZC_SEQ]); - if (zilog->zl_root_zio == NULL) { - zilog->zl_root_zio = zio_root(zilog->zl_spa, NULL, NULL, - ZIO_FLAG_CANFAIL); - } - if (lwb->lwb_zio == NULL) { + if (lwb->lwb_root_zio == NULL) { abd_t *lwb_abd = abd_get_from_buf(lwb->lwb_buf, BP_GET_LSIZE(&lwb->lwb_blk)); + if (!lwb->lwb_slog || zilog->zl_cur_used <= zil_slog_bulk) prio = ZIO_PRIORITY_SYNC_WRITE; else prio = ZIO_PRIORITY_ASYNC_WRITE; - lwb->lwb_zio = zio_rewrite(zilog->zl_root_zio, zilog->zl_spa, - 0, &lwb->lwb_blk, lwb_abd, BP_GET_LSIZE(&lwb->lwb_blk), - zil_lwb_write_done, lwb, prio, - ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_PROPAGATE, &zb); + + lwb->lwb_root_zio = zio_root(zilog->zl_spa, + zil_lwb_flush_vdevs_done, lwb, ZIO_FLAG_CANFAIL); + ASSERT3P(lwb->lwb_root_zio, !=, NULL); + + lwb->lwb_write_zio = zio_rewrite(lwb->lwb_root_zio, + zilog->zl_spa, 0, &lwb->lwb_blk, lwb_abd, + BP_GET_LSIZE(&lwb->lwb_blk), zil_lwb_write_done, lwb, + prio, ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_PROPAGATE, &zb); + ASSERT3P(lwb->lwb_write_zio, !=, NULL); + + lwb->lwb_state = LWB_STATE_OPENED; + + mutex_enter(&zilog->zl_lock); + + /* + * The zilog's "zl_last_lwb_opened" field is used to + * build the lwb/zio dependency chain, which is used to + * preserve the ordering of lwb completions that is + * required by the semantics of the ZIL. Each new lwb + * zio becomes a parent of the "previous" lwb zio, such + * that the new lwb's zio cannot complete until the + * "previous" lwb's zio completes. + * + * This is required by the semantics of zil_commit(); + * the commit waiters attached to the lwbs will be woken + * in the lwb zio's completion callback, so this zio + * dependency graph ensures the waiters are woken in the + * correct order (the same order the lwbs were created). + */ + lwb_t *last_lwb_opened = zilog->zl_last_lwb_opened; + if (last_lwb_opened != NULL && + last_lwb_opened->lwb_state != LWB_STATE_DONE) { + ASSERT(last_lwb_opened->lwb_state == LWB_STATE_OPENED || + last_lwb_opened->lwb_state == LWB_STATE_ISSUED); + ASSERT3P(last_lwb_opened->lwb_root_zio, !=, NULL); + zio_add_child(lwb->lwb_root_zio, + last_lwb_opened->lwb_root_zio); + } + zilog->zl_last_lwb_opened = lwb; + + mutex_exit(&zilog->zl_lock); } + + ASSERT3P(lwb->lwb_root_zio, !=, NULL); + ASSERT3P(lwb->lwb_write_zio, !=, NULL); + ASSERT3S(lwb->lwb_state, ==, LWB_STATE_OPENED); } /* @@ -943,7 +1181,7 @@ uint64_t zil_block_buckets[] = { * Calls are serialized. */ static lwb_t * -zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) +zil_lwb_write_issue(zilog_t *zilog, lwb_t *lwb) { lwb_t *nlwb = NULL; zil_chain_t *zilc; @@ -955,6 +1193,11 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) int i, error; boolean_t slog; + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); + ASSERT3P(lwb->lwb_root_zio, !=, NULL); + ASSERT3P(lwb->lwb_write_zio, !=, NULL); + ASSERT3S(lwb->lwb_state, ==, LWB_STATE_OPENED); + if (BP_GET_CHECKSUM(&lwb->lwb_blk) == ZIO_CHECKSUM_ZILOG2) { zilc = (zil_chain_t *)lwb->lwb_buf; bp = &zilc->zc_next_blk; @@ -974,6 +1217,7 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) * We dirty the dataset to ensure that zil_sync() will be called * to clean up in the event of allocation failure or I/O failure. */ + tx = dmu_tx_create(zilog->zl_os); /* @@ -1026,6 +1270,7 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) zilog->zl_prev_rotor = (zilog->zl_prev_rotor + 1) & (ZIL_PREV_BLKS - 1); BP_ZERO(bp); + /* pass the old blkptr in order to spread log blocks across devs */ error = zio_alloc_zil(spa, txg, bp, &lwb->lwb_blk, zil_blksz, &slog); if (error == 0) { @@ -1034,19 +1279,16 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) bp->blk_cksum.zc_word[ZIL_ZC_SEQ]++; /* - * Allocate a new log write buffer (lwb). + * Allocate a new log write block (lwb). */ nlwb = zil_alloc_lwb(zilog, bp, slog, txg); - - /* Record the block for later vdev flushing */ - zil_add_block(zilog, &lwb->lwb_blk); } if (BP_GET_CHECKSUM(&lwb->lwb_blk) == ZIO_CHECKSUM_ZILOG2) { /* For Slim ZIL only write what is used. */ wsz = P2ROUNDUP_TYPED(lwb->lwb_nused, ZIL_MIN_BLKSZ, uint64_t); ASSERT3U(wsz, <=, lwb->lwb_sz); - zio_shrink(lwb->lwb_zio, wsz); + zio_shrink(lwb->lwb_write_zio, wsz); } else { wsz = lwb->lwb_sz; @@ -1061,7 +1303,14 @@ zil_lwb_write_start(zilog_t *zilog, lwb_t *lwb) */ bzero(lwb->lwb_buf + lwb->lwb_nused, wsz - lwb->lwb_nused); - zio_nowait(lwb->lwb_zio); /* Kick off the write for the old log block */ + spa_config_enter(zilog->zl_spa, SCL_STATE, lwb, RW_READER); + + zil_lwb_add_block(lwb, &lwb->lwb_blk); + lwb->lwb_issued_timestamp = gethrtime(); + lwb->lwb_state = LWB_STATE_ISSUED; + + zio_nowait(lwb->lwb_root_zio); + zio_nowait(lwb->lwb_write_zio); /* * If there was an allocation failure then nlwb will be null which @@ -1078,13 +1327,33 @@ zil_lwb_commit(zilog_t *zilog, itx_t *itx, lwb_t *lwb) char *lr_buf; uint64_t dlen, dnow, lwb_sp, reclen, txg; - if (lwb == NULL) - return (NULL); + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); + ASSERT3P(lwb, !=, NULL); + ASSERT3P(lwb->lwb_buf, !=, NULL); + + zil_lwb_write_open(zilog, lwb); + + lrc = &itx->itx_lr; + lrw = (lr_write_t *)lrc; - ASSERT(lwb->lwb_buf != NULL); + /* + * A commit itx doesn't represent any on-disk state; instead + * it's simply used as a place holder on the commit list, and + * provides a mechanism for attaching a "commit waiter" onto the + * correct lwb (such that the waiter can be signalled upon + * completion of that lwb). Thus, we don't process this itx's + * log record if it's a commit itx (these itx's don't have log + * records), and instead link the itx's waiter onto the lwb's + * list of waiters. + * + * For more details, see the comment above zil_commit(). + */ + if (lrc->lrc_txtype == TX_COMMIT) { + zil_commit_waiter_link_lwb(itx->itx_private, lwb); + itx->itx_private = NULL; + return (lwb); + } - lrc = &itx->itx_lr; /* Common log record inside itx. */ - lrw = (lr_write_t *)lrc; /* Write log record inside itx. */ if (lrc->lrc_txtype == TX_WRITE && itx->itx_wr_state == WR_NEED_COPY) { dlen = P2ROUNDUP_TYPED( lrw->lr_length, sizeof (uint64_t), uint64_t); @@ -1095,7 +1364,7 @@ zil_lwb_commit(zilog_t *zilog, itx_t *itx, lwb_t *lwb) zilog->zl_cur_used += (reclen + dlen); txg = lrc->lrc_txg; - zil_lwb_write_init(zilog, lwb); + ASSERT3U(zilog->zl_cur_used, <, UINT64_MAX - (reclen + dlen)); cont: /* @@ -1106,10 +1375,10 @@ cont: if (reclen > lwb_sp || (reclen + dlen > lwb_sp && lwb_sp < ZIL_MAX_WASTE_SPACE && (dlen % ZIL_MAX_LOG_DATA == 0 || lwb_sp < reclen + dlen % ZIL_MAX_LOG_DATA))) { - lwb = zil_lwb_write_start(zilog, lwb); + lwb = zil_lwb_write_issue(zilog, lwb); if (lwb == NULL) return (NULL); - zil_lwb_write_init(zilog, lwb); + zil_lwb_write_open(zilog, lwb); ASSERT(LWB_EMPTY(lwb)); lwb_sp = lwb->lwb_sz - lwb->lwb_nused; ASSERT3U(reclen + MIN(dlen, sizeof (uint64_t)), <=, lwb_sp); @@ -1142,8 +1411,25 @@ cont: ASSERT(itx->itx_wr_state == WR_INDIRECT); dbuf = NULL; } - error = zilog->zl_get_data( - itx->itx_private, lrwb, dbuf, lwb->lwb_zio); + + /* + * We pass in the "lwb_write_zio" rather than + * "lwb_root_zio" so that the "lwb_write_zio" + * becomes the parent of any zio's created by + * the "zl_get_data" callback. The vdevs are + * flushed after the "lwb_write_zio" completes, + * so we want to make sure that completion + * callback waits for these additional zio's, + * such that the vdevs used by those zio's will + * be included in the lwb's vdev tree, and those + * vdevs will be properly flushed. If we passed + * in "lwb_root_zio" here, then these additional + * vdevs may not be flushed; e.g. if these zio's + * completed after "lwb_write_zio" completed. + */ + error = zilog->zl_get_data(itx->itx_private, + lrwb, dbuf, lwb, lwb->lwb_write_zio); + if (error == EIO) { txg_wait_synced(zilog->zl_dmu_pool, txg); return (lwb); @@ -1162,9 +1448,11 @@ cont: * equal to the itx sequence number because not all transactions * are synchronous, and sometimes spa_sync() gets there first. */ - lrcb->lrc_seq = ++zilog->zl_lr_seq; /* we are single threaded */ + lrcb->lrc_seq = ++zilog->zl_lr_seq; lwb->lwb_nused += reclen + dnow; - lwb->lwb_max_txg = MAX(lwb->lwb_max_txg, txg); + + zil_lwb_add_txg(lwb, txg); + ASSERT3U(lwb->lwb_nused, <=, lwb->lwb_sz); ASSERT0(P2PHASE(lwb->lwb_nused, sizeof (uint64_t))); @@ -1214,9 +1502,30 @@ zil_itxg_clean(itxs_t *itxs) list = &itxs->i_sync_list; while ((itx = list_head(list)) != NULL) { + /* + * In the general case, commit itxs will not be found + * here, as they'll be committed to an lwb via + * zil_lwb_commit(), and free'd in that function. Having + * said that, it is still possible for commit itxs to be + * found here, due to the following race: + * + * - a thread calls zil_commit() which assigns the + * commit itx to a per-txg i_sync_list + * - zil_itxg_clean() is called (e.g. via spa_sync()) + * while the waiter is still on the i_sync_list + * + * There's nothing to prevent syncing the txg while the + * waiter is on the i_sync_list. This normally doesn't + * happen because spa_sync() is slower than zil_commit(), + * but if zil_commit() calls txg_wait_synced() (e.g. + * because zil_create() or zil_commit_writer_stall() is + * called) we will hit this case. + */ + if (itx->itx_lr.lrc_txtype == TX_COMMIT) + zil_commit_waiter_skip(itx->itx_private); + list_remove(list, itx); - kmem_free(itx, offsetof(itx_t, itx_lr) + - itx->itx_lr.lrc_reclen); + zil_itx_destroy(itx); } cookie = NULL; @@ -1225,8 +1534,9 @@ zil_itxg_clean(itxs_t *itxs) list = &ian->ia_list; while ((itx = list_head(list)) != NULL) { list_remove(list, itx); - kmem_free(itx, offsetof(itx_t, itx_lr) + - itx->itx_lr.lrc_reclen); + /* commit itxs should never be on the async lists. */ + ASSERT3U(itx->itx_lr.lrc_txtype, !=, TX_COMMIT); + zil_itx_destroy(itx); } list_destroy(list); kmem_free(ian, sizeof (itx_async_node_t)); @@ -1291,8 +1601,9 @@ zil_remove_async(zilog_t *zilog, uint64_t oid) } while ((itx = list_head(&clean_list)) != NULL) { list_remove(&clean_list, itx); - kmem_free(itx, offsetof(itx_t, itx_lr) + - itx->itx_lr.lrc_reclen); + /* commit itxs should never be on the async lists. */ + ASSERT3U(itx->itx_lr.lrc_txtype, !=, TX_COMMIT); + zil_itx_destroy(itx); } list_destroy(&clean_list); } @@ -1368,7 +1679,14 @@ zil_itx_assign(zilog_t *zilog, itx_t *itx, dmu_tx_t *tx) } itx->itx_lr.lrc_txg = dmu_tx_get_txg(tx); - zilog_dirty(zilog, txg); + + /* + * We don't want to dirty the ZIL using ZILTEST_TXG, because + * zil_clean() will never be called using ZILTEST_TXG. Thus, we + * need to be careful to always dirty the ZIL using the "real" + * TXG (not itxg_txg) even when the SPA is frozen. + */ + zilog_dirty(zilog, dmu_tx_get_txg(tx)); mutex_exit(&itxg->itxg_lock); /* Release the old itxs now we've dropped the lock */ @@ -1389,6 +1707,8 @@ zil_clean(zilog_t *zilog, uint64_t synced_txg) itxg_t *itxg = &zilog->zl_itxg[synced_txg & TXG_MASK]; itxs_t *clean_me; + ASSERT3U(synced_txg, <, ZILTEST_TXG); + mutex_enter(&itxg->itxg_lock); if (itxg->itxg_itxs == NULL || itxg->itxg_txg == ZILTEST_TXG) { mutex_exit(&itxg->itxg_lock); @@ -1414,7 +1734,8 @@ zil_clean(zilog_t *zilog, uint64_t synced_txg) } /* - * Get the list of itxs to commit into zl_itx_commit_list. + * This function will traverse the queue of itxs that need to be + * committed, and move them onto the ZIL's zl_itx_commit_list. */ static void zil_get_commit_list(zilog_t *zilog) @@ -1422,6 +1743,8 @@ zil_get_commit_list(zilog_t *zilog) uint64_t otxg, txg; list_t *commit_list = &zilog->zl_itx_commit_list; + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); + if (spa_freeze_txg(zilog->zl_spa) != UINT64_MAX) /* ziltest support */ otxg = ZILTEST_TXG; else @@ -1513,142 +1836,787 @@ zil_async_to_sync(zilog_t *zilog, uint64_t foid) } } +/* + * This function will prune commit itxs that are at the head of the + * commit list (it won't prune past the first non-commit itx), and + * either: a) attach them to the last lwb that's still pending + * completion, or b) skip them altogether. + * + * This is used as a performance optimization to prevent commit itxs + * from generating new lwbs when it's unnecessary to do so. + */ static void -zil_commit_writer(zilog_t *zilog) +zil_prune_commit_list(zilog_t *zilog) { - uint64_t txg; itx_t *itx; - lwb_t *lwb; - spa_t *spa = zilog->zl_spa; - int error = 0; - ASSERT(zilog->zl_root_zio == NULL); + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); - mutex_exit(&zilog->zl_lock); + while (itx = list_head(&zilog->zl_itx_commit_list)) { + lr_t *lrc = &itx->itx_lr; + if (lrc->lrc_txtype != TX_COMMIT) + break; - zil_get_commit_list(zilog); + mutex_enter(&zilog->zl_lock); + + lwb_t *last_lwb = zilog->zl_last_lwb_opened; + if (last_lwb == NULL || last_lwb->lwb_state == LWB_STATE_DONE) { + /* + * All of the itxs this waiter was waiting on + * must have already completed (or there were + * never any itx's for it to wait on), so it's + * safe to skip this waiter and mark it done. + */ + zil_commit_waiter_skip(itx->itx_private); + } else { + zil_commit_waiter_link_lwb(itx->itx_private, last_lwb); + itx->itx_private = NULL; + } + + mutex_exit(&zilog->zl_lock); + + list_remove(&zilog->zl_itx_commit_list, itx); + zil_itx_destroy(itx); + } + + IMPLY(itx != NULL, itx->itx_lr.lrc_txtype != TX_COMMIT); +} + +static void +zil_commit_writer_stall(zilog_t *zilog) +{ + /* + * When zio_alloc_zil() fails to allocate the next lwb block on + * disk, we must call txg_wait_synced() to ensure all of the + * lwbs in the zilog's zl_lwb_list are synced and then freed (in + * zil_sync()), such that any subsequent ZIL writer (i.e. a call + * to zil_process_commit_list()) will have to call zil_create(), + * and start a new ZIL chain. + * + * Since zil_alloc_zil() failed, the lwb that was previously + * issued does not have a pointer to the "next" lwb on disk. + * Thus, if another ZIL writer thread was to allocate the "next" + * on-disk lwb, that block could be leaked in the event of a + * crash (because the previous lwb on-disk would not point to + * it). + * + * We must hold the zilog's zl_writer_lock while we do this, to + * ensure no new threads enter zil_process_commit_list() until + * all lwb's in the zl_lwb_list have been synced and freed + * (which is achieved via the txg_wait_synced() call). + */ + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); + txg_wait_synced(zilog->zl_dmu_pool, 0); + ASSERT3P(list_tail(&zilog->zl_lwb_list), ==, NULL); +} + +/* + * This function will traverse the commit list, creating new lwbs as + * needed, and committing the itxs from the commit list to these newly + * created lwbs. Additionally, as a new lwb is created, the previous + * lwb will be issued to the zio layer to be written to disk. + */ +static void +zil_process_commit_list(zilog_t *zilog) +{ + spa_t *spa = zilog->zl_spa; + list_t nolwb_waiters; + lwb_t *lwb; + itx_t *itx; + + ASSERT(MUTEX_HELD(&zilog->zl_writer_lock)); /* * Return if there's nothing to commit before we dirty the fs by * calling zil_create(). */ - if (list_head(&zilog->zl_itx_commit_list) == NULL) { - mutex_enter(&zilog->zl_lock); + if (list_head(&zilog->zl_itx_commit_list) == NULL) return; - } - if (zilog->zl_suspend) { - lwb = NULL; + list_create(&nolwb_waiters, sizeof (zil_commit_waiter_t), + offsetof(zil_commit_waiter_t, zcw_node)); + + lwb = list_tail(&zilog->zl_lwb_list); + if (lwb == NULL) { + lwb = zil_create(zilog); } else { - lwb = list_tail(&zilog->zl_lwb_list); - if (lwb == NULL) - lwb = zil_create(zilog); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_ISSUED); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_DONE); } - DTRACE_PROBE1(zil__cw1, zilog_t *, zilog); while (itx = list_head(&zilog->zl_itx_commit_list)) { - txg = itx->itx_lr.lrc_txg; + lr_t *lrc = &itx->itx_lr; + uint64_t txg = lrc->lrc_txg; + ASSERT3U(txg, !=, 0); + if (lrc->lrc_txtype == TX_COMMIT) { + DTRACE_PROBE2(zil__process__commit__itx, + zilog_t *, zilog, itx_t *, itx); + } else { + DTRACE_PROBE2(zil__process__normal__itx, + zilog_t *, zilog, itx_t *, itx); + } + /* * This is inherently racy and may result in us writing - * out a log block for a txg that was just synced. This is - * ok since we'll end cleaning up that log block the next - * time we call zil_sync(). + * out a log block for a txg that was just synced. This + * is ok since we'll end cleaning up that log block the + * next time we call zil_sync(). */ - if (txg > spa_last_synced_txg(spa) || txg > spa_freeze_txg(spa)) - lwb = zil_lwb_commit(zilog, itx, lwb); + boolean_t synced = txg <= spa_last_synced_txg(spa); + boolean_t frozen = txg > spa_freeze_txg(spa); + + if (!synced || frozen) { + if (lwb != NULL) { + lwb = zil_lwb_commit(zilog, itx, lwb); + } else if (lrc->lrc_txtype == TX_COMMIT) { + ASSERT3P(lwb, ==, NULL); + zil_commit_waiter_link_nolwb( + itx->itx_private, &nolwb_waiters); + } + } else if (lrc->lrc_txtype == TX_COMMIT) { + ASSERT3B(synced, ==, B_TRUE); + ASSERT3B(frozen, ==, B_FALSE); + + /* + * If this is a commit itx, then there will be a + * thread that is either: already waiting for + * it, or soon will be waiting. + * + * This itx has already been committed to disk + * via spa_sync() so we don't bother committing + * it to an lwb. As a result, we cannot use the + * lwb zio callback to signal the waiter and + * mark it as done, so we must do that here. + */ + zil_commit_waiter_skip(itx->itx_private); + } + list_remove(&zilog->zl_itx_commit_list, itx); - kmem_free(itx, offsetof(itx_t, itx_lr) - + itx->itx_lr.lrc_reclen); + zil_itx_destroy(itx); } DTRACE_PROBE1(zil__cw2, zilog_t *, zilog); - /* write the last block out */ - if (lwb != NULL && lwb->lwb_zio != NULL) - lwb = zil_lwb_write_start(zilog, lwb); + if (lwb == NULL) { + /* + * This indicates zio_alloc_zil() failed to allocate the + * "next" lwb on-disk. When this happens, we must stall + * the ZIL write pipeline; see the comment within + * zil_commit_writer_stall() for more details. + */ + zil_commit_writer_stall(zilog); - zilog->zl_cur_used = 0; + /* + * Additionally, we have to signal and mark the "nolwb" + * waiters as "done" here, since without an lwb, we + * can't do this via zil_lwb_flush_vdevs_done() like + * normal. + */ + zil_commit_waiter_t *zcw; + while (zcw = list_head(&nolwb_waiters)) { + zil_commit_waiter_skip(zcw); + list_remove(&nolwb_waiters, zcw); + } + } else { + ASSERT(list_is_empty(&nolwb_waiters)); + ASSERT3P(lwb, !=, NULL); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_ISSUED); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_DONE); + + /* + * At this point, the ZIL block pointed at by the "lwb" + * variable is in one of the following states: "closed" + * or "open". + * + * If its "closed", then no itxs have been committed to + * it, so there's no point in issuing its zio (i.e. + * it's "empty"). + * + * If its "open" state, then it contains one or more + * itxs that eventually need to be committed to stable + * storage. In this case we intentionally do not issue + * the lwb's zio to disk yet, and instead rely on one of + * the following two mechanisms for issuing the zio: + * + * 1. Ideally, there will be more ZIL activity occuring + * on the system, such that this function will be + * immediately called again (not necessarily by the same + * thread) and this lwb's zio will be issued via + * zil_lwb_commit(). This way, the lwb is guaranteed to + * be "full" when it is issued to disk, and we'll make + * use of the lwb's size the best we can. + * + * 2. If there isn't sufficient ZIL activity occuring on + * the system, such that this lwb's zio isn't issued via + * zil_lwb_commit(), zil_commit_waiter() will issue the + * lwb's zio. If this occurs, the lwb is not guaranteed + * to be "full" by the time its zio is issued, and means + * the size of the lwb was "too large" given the amount + * of ZIL activity occuring on the system at that time. + * + * We do this for a couple of reasons: + * + * 1. To try and reduce the number of IOPs needed to + * write the same number of itxs. If an lwb has space + * available in it's buffer for more itxs, and more itxs + * will be committed relatively soon (relative to the + * latency of performing a write), then it's beneficial + * to wait for these "next" itxs. This way, more itxs + * can be committed to stable storage with fewer writes. + * + * 2. To try and use the largest lwb block size that the + * incoming rate of itxs can support. Again, this is to + * try and pack as many itxs into as few lwbs as + * possible, without significantly impacting the latency + * of each individual itx. + */ + } +} + +/* + * This function is responsible for ensuring the passed in commit waiter + * (and associated commit itx) is committed to an lwb. If the waiter is + * not already committed to an lwb, all itxs in the zilog's queue of + * itxs will be processed. The assumption is the passed in waiter's + * commit itx will found in the queue just like the other non-commit + * itxs, such that when the entire queue is processed, the waiter will + * have been commited to an lwb. + * + * The lwb associated with the passed in waiter is not guaranteed to + * have been issued by the time this function completes. If the lwb is + * not issued, we rely on future calls to zil_commit_writer() to issue + * the lwb, or the timeout mechanism found in zil_commit_waiter(). + */ +static void +zil_commit_writer(zilog_t *zilog, zil_commit_waiter_t *zcw) +{ + ASSERT(!MUTEX_HELD(&zilog->zl_lock)); + ASSERT(spa_writeable(zilog->zl_spa)); + ASSERT0(zilog->zl_suspend); + + mutex_enter(&zilog->zl_writer_lock); + + if (zcw->zcw_lwb != NULL || zcw->zcw_done) { + /* + * It's possible that, while we were waiting to acquire + * the "zl_writer_lock", another thread committed this + * waiter to an lwb. If that occurs, we bail out early, + * without processing any of the zilog's queue of itxs. + * + * On certain workloads and system configurations, the + * "zl_writer_lock" can become highly contended. In an + * attempt to reduce this contention, we immediately drop + * the lock if the waiter has already been processed. + * + * We've measured this optimization to reduce CPU spent + * contending on this lock by up to 5%, using a system + * with 32 CPUs, low latency storage (~50 usec writes), + * and 1024 threads performing sync writes. + */ + goto out; + } + + zil_get_commit_list(zilog); + zil_prune_commit_list(zilog); + zil_process_commit_list(zilog); + +out: + mutex_exit(&zilog->zl_writer_lock); +} + +static void +zil_commit_waiter_timeout(zilog_t *zilog, zil_commit_waiter_t *zcw) +{ + ASSERT(!MUTEX_HELD(&zilog->zl_writer_lock)); + ASSERT(MUTEX_HELD(&zcw->zcw_lock)); + ASSERT3B(zcw->zcw_done, ==, B_FALSE); + + lwb_t *lwb = zcw->zcw_lwb; + ASSERT3P(lwb, !=, NULL); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_CLOSED); + + /* + * If the lwb has already been issued by another thread, we can + * immediately return since there's no work to be done (the + * point of this function is to issue the lwb). Additionally, we + * do this prior to acquiring the zl_writer_lock, to avoid + * acquiring it when it's not necessary to do so. + */ + if (lwb->lwb_state == LWB_STATE_ISSUED || + lwb->lwb_state == LWB_STATE_DONE) + return; + + /* + * In order to call zil_lwb_write_issue() we must hold the + * zilog's "zl_writer_lock". We can't simply acquire that lock, + * since we're already holding the commit waiter's "zcw_lock", + * and those two locks are aquired in the opposite order + * elsewhere. + */ + mutex_exit(&zcw->zcw_lock); + mutex_enter(&zilog->zl_writer_lock); + mutex_enter(&zcw->zcw_lock); + + /* + * Since we just dropped and re-acquired the commit waiter's + * lock, we have to re-check to see if the waiter was marked + * "done" during that process. If the waiter was marked "done", + * the "lwb" pointer is no longer valid (it can be free'd after + * the waiter is marked "done"), so without this check we could + * wind up with a use-after-free error below. + */ + if (zcw->zcw_done) + goto out; + + ASSERT3P(lwb, ==, zcw->zcw_lwb); + + /* + * We've already checked this above, but since we hadn't + * acquired the zilog's zl_writer_lock, we have to perform this + * check a second time while holding the lock. We can't call + * zil_lwb_write_issue() if the lwb had already been issued. + */ + if (lwb->lwb_state == LWB_STATE_ISSUED || + lwb->lwb_state == LWB_STATE_DONE) + goto out; + + ASSERT3S(lwb->lwb_state, ==, LWB_STATE_OPENED); + + /* + * As described in the comments above zil_commit_waiter() and + * zil_process_commit_list(), we need to issue this lwb's zio + * since we've reached the commit waiter's timeout and it still + * hasn't been issued. + */ + lwb_t *nlwb = zil_lwb_write_issue(zilog, lwb); + + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_OPENED); /* - * Wait if necessary for the log blocks to be on stable storage. + * Since the lwb's zio hadn't been issued by the time this thread + * reached its timeout, we reset the zilog's "zl_cur_used" field + * to influence the zil block size selection algorithm. + * + * By having to issue the lwb's zio here, it means the size of the + * lwb was too large, given the incoming throughput of itxs. By + * setting "zl_cur_used" to zero, we communicate this fact to the + * block size selection algorithm, so it can take this informaiton + * into account, and potentially select a smaller size for the + * next lwb block that is allocated. */ - if (zilog->zl_root_zio) { - error = zio_wait(zilog->zl_root_zio); - zilog->zl_root_zio = NULL; - zil_flush_vdevs(zilog); + zilog->zl_cur_used = 0; + + if (nlwb == NULL) { + /* + * When zil_lwb_write_issue() returns NULL, this + * indicates zio_alloc_zil() failed to allocate the + * "next" lwb on-disk. When this occurs, the ZIL write + * pipeline must be stalled; see the comment within the + * zil_commit_writer_stall() function for more details. + * + * We must drop the commit waiter's lock prior to + * calling zil_commit_writer_stall() or else we can wind + * up with the following deadlock: + * + * - This thread is waiting for the txg to sync while + * holding the waiter's lock; txg_wait_synced() is + * used within txg_commit_writer_stall(). + * + * - The txg can't sync because it is waiting for this + * lwb's zio callback to call dmu_tx_commit(). + * + * - The lwb's zio callback can't call dmu_tx_commit() + * because it's blocked trying to acquire the waiter's + * lock, which occurs prior to calling dmu_tx_commit() + */ + mutex_exit(&zcw->zcw_lock); + zil_commit_writer_stall(zilog); + mutex_enter(&zcw->zcw_lock); } - if (error || lwb == NULL) - txg_wait_synced(zilog->zl_dmu_pool, 0); +out: + mutex_exit(&zilog->zl_writer_lock); + ASSERT(MUTEX_HELD(&zcw->zcw_lock)); +} - mutex_enter(&zilog->zl_lock); +/* + * This function is responsible for performing the following two tasks: + * + * 1. its primary responsibility is to block until the given "commit + * waiter" is considered "done". + * + * 2. its secondary responsibility is to issue the zio for the lwb that + * the given "commit waiter" is waiting on, if this function has + * waited "long enough" and the lwb is still in the "open" state. + * + * Given a sufficient amount of itxs being generated and written using + * the ZIL, the lwb's zio will be issued via the zil_lwb_commit() + * function. If this does not occur, this secondary responsibility will + * ensure the lwb is issued even if there is not other synchronous + * activity on the system. + * + * For more details, see zil_process_commit_list(); more specifically, + * the comment at the bottom of that function. + */ +static void +zil_commit_waiter(zilog_t *zilog, zil_commit_waiter_t *zcw) +{ + ASSERT(!MUTEX_HELD(&zilog->zl_lock)); + ASSERT(!MUTEX_HELD(&zilog->zl_writer_lock)); + ASSERT(spa_writeable(zilog->zl_spa)); + ASSERT0(zilog->zl_suspend); + + mutex_enter(&zcw->zcw_lock); /* - * Remember the highest committed log sequence number for ztest. - * We only update this value when all the log writes succeeded, - * because ztest wants to ASSERT that it got the whole log chain. + * The timeout is scaled based on the lwb latency to avoid + * significantly impacting the latency of each individual itx. + * For more details, see the comment at the bottom of the + * zil_process_commit_list() function. */ - if (error == 0 && lwb != NULL) - zilog->zl_commit_lr_seq = zilog->zl_lr_seq; + int pct = MAX(zfs_commit_timeout_pct, 1); + hrtime_t sleep = (zilog->zl_last_lwb_latency * pct) / 100; + hrtime_t wakeup = gethrtime() + sleep; + boolean_t timedout = B_FALSE; + + while (!zcw->zcw_done) { + ASSERT(MUTEX_HELD(&zcw->zcw_lock)); + + lwb_t *lwb = zcw->zcw_lwb; + + /* + * Usually, the waiter will have a non-NULL lwb field here, + * but it's possible for it to be NULL as a result of + * zil_commit() racing with spa_sync(). + * + * When zil_clean() is called, it's possible for the itxg + * list (which may be cleaned via a taskq) to contain + * commit itxs. When this occurs, the commit waiters linked + * off of these commit itxs will not be committed to an + * lwb. Additionally, these commit waiters will not be + * marked done until zil_commit_waiter_skip() is called via + * zil_itxg_clean(). + * + * Thus, it's possible for this commit waiter (i.e. the + * "zcw" variable) to be found in this "in between" state; + * where it's "zcw_lwb" field is NULL, and it hasn't yet + * been skipped, so it's "zcw_done" field is still B_FALSE. + */ + IMPLY(lwb != NULL, lwb->lwb_state != LWB_STATE_CLOSED); + + if (lwb != NULL && lwb->lwb_state == LWB_STATE_OPENED) { + ASSERT3B(timedout, ==, B_FALSE); + + /* + * If the lwb hasn't been issued yet, then we + * need to wait with a timeout, in case this + * function needs to issue the lwb after the + * timeout is reached; responsibility (2) from + * the comment above this function. + */ + clock_t timeleft = cv_timedwait_hires(&zcw->zcw_cv, + &zcw->zcw_lock, wakeup, USEC2NSEC(1), + CALLOUT_FLAG_ABSOLUTE); + + if (timeleft >= 0 || zcw->zcw_done) + continue; + + timedout = B_TRUE; + zil_commit_waiter_timeout(zilog, zcw); + + if (!zcw->zcw_done) { + /* + * If the commit waiter has already been + * marked "done", it's possible for the + * waiter's lwb structure to have already + * been freed. Thus, we can only reliably + * make these assertions if the waiter + * isn't done. + */ + ASSERT3P(lwb, ==, zcw->zcw_lwb); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_OPENED); + } + } else { + /* + * If the lwb isn't open, then it must have already + * been issued. In that case, there's no need to + * use a timeout when waiting for the lwb to + * complete. + * + * Additionally, if the lwb is NULL, the waiter + * will soon be signalled and marked done via + * zil_clean() and zil_itxg_clean(), so no timeout + * is required. + */ + + IMPLY(lwb != NULL, + lwb->lwb_state == LWB_STATE_ISSUED || + lwb->lwb_state == LWB_STATE_DONE); + cv_wait(&zcw->zcw_cv, &zcw->zcw_lock); + } + } + + mutex_exit(&zcw->zcw_lock); +} + +static zil_commit_waiter_t * +zil_alloc_commit_waiter() +{ + zil_commit_waiter_t *zcw = kmem_cache_alloc(zil_zcw_cache, KM_SLEEP); + + cv_init(&zcw->zcw_cv, NULL, CV_DEFAULT, NULL); + mutex_init(&zcw->zcw_lock, NULL, MUTEX_DEFAULT, NULL); + list_link_init(&zcw->zcw_node); + zcw->zcw_lwb = NULL; + zcw->zcw_done = B_FALSE; + zcw->zcw_zio_error = 0; + + return (zcw); +} + +static void +zil_free_commit_waiter(zil_commit_waiter_t *zcw) +{ + ASSERT(!list_link_active(&zcw->zcw_node)); + ASSERT3P(zcw->zcw_lwb, ==, NULL); + ASSERT3B(zcw->zcw_done, ==, B_TRUE); + mutex_destroy(&zcw->zcw_lock); + cv_destroy(&zcw->zcw_cv); + kmem_cache_free(zil_zcw_cache, zcw); +} + +/* + * This function is used to create a TX_COMMIT itx and assign it. This + * way, it will be linked into the ZIL's list of synchronous itxs, and + * then later committed to an lwb (or skipped) when + * zil_process_commit_list() is called. + */ +static void +zil_commit_itx_assign(zilog_t *zilog, zil_commit_waiter_t *zcw) +{ + dmu_tx_t *tx = dmu_tx_create(zilog->zl_os); + VERIFY0(dmu_tx_assign(tx, TXG_WAIT)); + + itx_t *itx = zil_itx_create(TX_COMMIT, sizeof (lr_t)); + itx->itx_sync = B_TRUE; + itx->itx_private = zcw; + + zil_itx_assign(zilog, itx, tx); + + dmu_tx_commit(tx); } /* - * Commit zfs transactions to stable storage. - * If foid is 0 push out all transactions, otherwise push only those - * for that object or might reference that object. + * Commit ZFS Intent Log transactions (itxs) to stable storage. + * + * When writing ZIL transactions to the on-disk representation of the + * ZIL, the itxs are committed to a Log Write Block (lwb). Multiple + * itxs can be committed to a single lwb. Once a lwb is written and + * committed to stable storage (i.e. the lwb is written, and vdevs have + * been flushed), each itx that was committed to that lwb is also + * considered to be committed to stable storage. + * + * When an itx is committed to an lwb, the log record (lr_t) contained + * by the itx is copied into the lwb's zio buffer, and once this buffer + * is written to disk, it becomes an on-disk ZIL block. + * + * As itxs are generated, they're inserted into the ZIL's queue of + * uncommitted itxs. The semantics of zil_commit() are such that it will + * block until all itxs that were in the queue when it was called, are + * committed to stable storage. + * + * If "foid" is zero, this means all "synchronous" and "asynchronous" + * itxs, for all objects in the dataset, will be committed to stable + * storage prior to zil_commit() returning. If "foid" is non-zero, all + * "synchronous" itxs for all objects, but only "asynchronous" itxs + * that correspond to the foid passed in, will be committed to stable + * storage prior to zil_commit() returning. + * + * Generally speaking, when zil_commit() is called, the consumer doesn't + * actually care about _all_ of the uncommitted itxs. Instead, they're + * simply trying to waiting for a specific itx to be committed to disk, + * but the interface(s) for interacting with the ZIL don't allow such + * fine-grained communication. A better interface would allow a consumer + * to create and assign an itx, and then pass a reference to this itx to + * zil_commit(); such that zil_commit() would return as soon as that + * specific itx was committed to disk (instead of waiting for _all_ + * itxs to be committed). + * + * When a thread calls zil_commit() a special "commit itx" will be + * generated, along with a corresponding "waiter" for this commit itx. + * zil_commit() will wait on this waiter's CV, such that when the waiter + * is marked done, and signalled, zil_commit() will return. + * + * This commit itx is inserted into the queue of uncommitted itxs. This + * provides an easy mechanism for determining which itxs were in the + * queue prior to zil_commit() having been called, and which itxs were + * added after zil_commit() was called. * - * itxs are committed in batches. In a heavily stressed zil there will be - * a commit writer thread who is writing out a bunch of itxs to the log - * for a set of committing threads (cthreads) in the same batch as the writer. - * Those cthreads are all waiting on the same cv for that batch. + * The commit it is special; it doesn't have any on-disk representation. + * When a commit itx is "committed" to an lwb, the waiter associated + * with it is linked onto the lwb's list of waiters. Then, when that lwb + * completes, each waiter on the lwb's list is marked done and signalled + * -- allowing the thread waiting on the waiter to return from zil_commit(). * - * There will also be a different and growing batch of threads that are - * waiting to commit (qthreads). When the committing batch completes - * a transition occurs such that the cthreads exit and the qthreads become - * cthreads. One of the new cthreads becomes the writer thread for the - * batch. Any new threads arriving become new qthreads. + * It's important to point out a few critical factors that allow us + * to make use of the commit itxs, commit waiters, per-lwb lists of + * commit waiters, and zio completion callbacks like we're doing: * - * Only 2 condition variables are needed and there's no transition - * between the two cvs needed. They just flip-flop between qthreads - * and cthreads. + * 1. The list of waiters for each lwb is traversed, and each commit + * waiter is marked "done" and signalled, in the zio completion + * callback of the lwb's zio[*]. * - * Using this scheme we can efficiently wakeup up only those threads - * that have been committed. + * * Actually, the waiters are signalled in the zio completion + * callback of the root zio for the DKIOCFLUSHWRITECACHE commands + * that are sent to the vdevs upon completion of the lwb zio. + * + * 2. When the itxs are inserted into the ZIL's queue of uncommitted + * itxs, the order in which they are inserted is preserved[*]; as + * itxs are added to the queue, they are added to the tail of + * in-memory linked lists. + * + * When committing the itxs to lwbs (to be written to disk), they + * are committed in the same order in which the itxs were added to + * the uncommitted queue's linked list(s); i.e. the linked list of + * itxs to commit is traversed from head to tail, and each itx is + * committed to an lwb in that order. + * + * * To clarify: + * + * - the order of "sync" itxs is preserved w.r.t. other + * "sync" itxs, regardless of the corresponding objects. + * - the order of "async" itxs is preserved w.r.t. other + * "async" itxs corresponding to the same object. + * - the order of "async" itxs is *not* preserved w.r.t. other + * "async" itxs corresponding to different objects. + * - the order of "sync" itxs w.r.t. "async" itxs (or vice + * versa) is *not* preserved, even for itxs that correspond + * to the same object. + * + * For more details, see: zil_itx_assign(), zil_async_to_sync(), + * zil_get_commit_list(), and zil_process_commit_list(). + * + * 3. The lwbs represent a linked list of blocks on disk. Thus, any + * lwb cannot be considered committed to stable storage, until its + * "previous" lwb is also committed to stable storage. This fact, + * coupled with the fact described above, means that itxs are + * committed in (roughly) the order in which they were generated. + * This is essential because itxs are dependent on prior itxs. + * Thus, we *must not* deem an itx as being committed to stable + * storage, until *all* prior itxs have also been committed to + * stable storage. + * + * To enforce this ordering of lwb zio's, while still leveraging as + * much of the underlying storage performance as possible, we rely + * on two fundamental concepts: + * + * 1. The creation and issuance of lwb zio's is protected by + * the zilog's "zl_writer_lock", which ensures only a single + * thread is creating and/or issuing lwb's at a time + * 2. The "previous" lwb is a child of the "current" lwb + * (leveraging the zio parent-child depenency graph) + * + * By relying on this parent-child zio relationship, we can have + * many lwb zio's concurrently issued to the underlying storage, + * but the order in which they complete will be the same order in + * which they were created. */ void zil_commit(zilog_t *zilog, uint64_t foid) { - uint64_t mybatch; + /* + * We should never attempt to call zil_commit on a snapshot for + * a couple of reasons: + * + * 1. A snapshot may never be modified, thus it cannot have any + * in-flight itxs that would have modified the dataset. + * + * 2. By design, when zil_commit() is called, a commit itx will + * be assigned to this zilog; as a result, the zilog will be + * dirtied. We must not dirty the zilog of a snapshot; there's + * checks in the code that enforce this invariant, and will + * cause a panic if it's not upheld. + */ + ASSERT3B(dmu_objset_is_snapshot(zilog->zl_os), ==, B_FALSE); if (zilog->zl_sync == ZFS_SYNC_DISABLED) return; - /* move the async itxs for the foid to the sync queues */ - zil_async_to_sync(zilog, foid); + if (!spa_writeable(zilog->zl_spa)) { + /* + * If the SPA is not writable, there should never be any + * pending itxs waiting to be committed to disk. If that + * weren't true, we'd skip writing those itxs out, and + * would break the sematics of zil_commit(); thus, we're + * verifying that truth before we return to the caller. + */ + ASSERT(list_is_empty(&zilog->zl_lwb_list)); + ASSERT3P(zilog->zl_last_lwb_opened, ==, NULL); + for (int i = 0; i < TXG_SIZE; i++) + ASSERT3P(zilog->zl_itxg[i].itxg_itxs, ==, NULL); + return; + } - mutex_enter(&zilog->zl_lock); - mybatch = zilog->zl_next_batch; - while (zilog->zl_writer) { - cv_wait(&zilog->zl_cv_batch[mybatch & 1], &zilog->zl_lock); - if (mybatch <= zilog->zl_com_batch) { - mutex_exit(&zilog->zl_lock); - return; - } + /* + * If the ZIL is suspended, we don't want to dirty it by calling + * zil_commit_itx_assign() below, nor can we write out + * lwbs like would be done in zil_commit_write(). Thus, we + * simply rely on txg_wait_synced() to maintain the necessary + * semantics, and avoid calling those functions altogether. + */ + if (zilog->zl_suspend > 0) { + txg_wait_synced(zilog->zl_dmu_pool, 0); + return; } - zilog->zl_next_batch++; - zilog->zl_writer = B_TRUE; - zil_commit_writer(zilog); - zilog->zl_com_batch = mybatch; - zilog->zl_writer = B_FALSE; - mutex_exit(&zilog->zl_lock); + /* + * Move the "async" itxs for the specified foid to the "sync" + * queues, such that they will be later committed (or skipped) + * to an lwb when zil_process_commit_list() is called. + * + * Since these "async" itxs must be committed prior to this + * call to zil_commit returning, we must perform this operation + * before we call zil_commit_itx_assign(). + */ + zil_async_to_sync(zilog, foid); - /* wake up one thread to become the next writer */ - cv_signal(&zilog->zl_cv_batch[(mybatch+1) & 1]); + /* + * We allocate a new "waiter" structure which will initially be + * linked to the commit itx using the itx's "itx_private" field. + * Since the commit itx doesn't represent any on-disk state, + * when it's committed to an lwb, rather than copying the its + * lr_t into the lwb's buffer, the commit itx's "waiter" will be + * added to the lwb's list of waiters. Then, when the lwb is + * committed to stable storage, each waiter in the lwb's list of + * waiters will be marked "done", and signalled. + * + * We must create the waiter and assign the commit itx prior to + * calling zil_commit_writer(), or else our specific commit itx + * is not guaranteed to be committed to an lwb prior to calling + * zil_commit_waiter(). + */ + zil_commit_waiter_t *zcw = zil_alloc_commit_waiter(); + zil_commit_itx_assign(zilog, zcw); + + zil_commit_writer(zilog, zcw); + zil_commit_waiter(zilog, zcw); - /* wake up all threads waiting for this batch to be committed */ - cv_broadcast(&zilog->zl_cv_batch[mybatch & 1]); + if (zcw->zcw_zio_error != 0) { + /* + * If there was an error writing out the ZIL blocks that + * this thread is waiting on, then we fallback to + * relying on spa_sync() to write out the data this + * thread is waiting on. Obviously this has performance + * implications, but the expectation is for this to be + * an exceptional case, and shouldn't occur often. + */ + DTRACE_PROBE2(zil__commit__io__error, + zilog_t *, zilog, zil_commit_waiter_t *, zcw); + txg_wait_synced(zilog->zl_dmu_pool, 0); + } + + zil_free_commit_waiter(zcw); } /* @@ -1707,8 +2675,8 @@ zil_sync(zilog_t *zilog, dmu_tx_t *tx) if (lwb->lwb_buf != NULL || lwb->lwb_max_txg > txg) break; list_remove(&zilog->zl_lwb_list, lwb); - zio_free_zil(spa, txg, &lwb->lwb_blk); - kmem_cache_free(zil_lwb_cache, lwb); + zio_free(spa, txg, &lwb->lwb_blk); + zil_free_lwb(zilog, lwb); /* * If we don't have anything left in the lwb list then @@ -1722,16 +2690,43 @@ zil_sync(zilog_t *zilog, dmu_tx_t *tx) mutex_exit(&zilog->zl_lock); } +/* ARGSUSED */ +static int +zil_lwb_cons(void *vbuf, void *unused, int kmflag) +{ + lwb_t *lwb = vbuf; + list_create(&lwb->lwb_waiters, sizeof (zil_commit_waiter_t), + offsetof(zil_commit_waiter_t, zcw_node)); + avl_create(&lwb->lwb_vdev_tree, zil_lwb_vdev_compare, + sizeof (zil_vdev_node_t), offsetof(zil_vdev_node_t, zv_node)); + mutex_init(&lwb->lwb_vdev_lock, NULL, MUTEX_DEFAULT, NULL); + return (0); +} + +/* ARGSUSED */ +static void +zil_lwb_dest(void *vbuf, void *unused) +{ + lwb_t *lwb = vbuf; + mutex_destroy(&lwb->lwb_vdev_lock); + avl_destroy(&lwb->lwb_vdev_tree); + list_destroy(&lwb->lwb_waiters); +} + void zil_init(void) { zil_lwb_cache = kmem_cache_create("zil_lwb_cache", - sizeof (struct lwb), 0, NULL, NULL, NULL, NULL, NULL, 0); + sizeof (lwb_t), 0, zil_lwb_cons, zil_lwb_dest, NULL, NULL, NULL, 0); + + zil_zcw_cache = kmem_cache_create("zil_zcw_cache", + sizeof (zil_commit_waiter_t), 0, NULL, NULL, NULL, NULL, NULL, 0); } void zil_fini(void) { + kmem_cache_destroy(zil_zcw_cache); kmem_cache_destroy(zil_lwb_cache); } @@ -1761,9 +2756,12 @@ zil_alloc(objset_t *os, zil_header_t *zh_phys) zilog->zl_destroy_txg = TXG_INITIAL - 1; zilog->zl_logbias = dmu_objset_logbias(os); zilog->zl_sync = dmu_objset_syncprop(os); - zilog->zl_next_batch = 1; + zilog->zl_dirty_max_txg = 0; + zilog->zl_last_lwb_opened = NULL; + zilog->zl_last_lwb_latency = 0; mutex_init(&zilog->zl_lock, NULL, MUTEX_DEFAULT, NULL); + mutex_init(&zilog->zl_writer_lock, NULL, MUTEX_DEFAULT, NULL); for (int i = 0; i < TXG_SIZE; i++) { mutex_init(&zilog->zl_itxg[i].itxg_lock, NULL, @@ -1776,15 +2774,7 @@ zil_alloc(objset_t *os, zil_header_t *zh_phys) list_create(&zilog->zl_itx_commit_list, sizeof (itx_t), offsetof(itx_t, itx_node)); - mutex_init(&zilog->zl_vdev_lock, NULL, MUTEX_DEFAULT, NULL); - - avl_create(&zilog->zl_vdev_tree, zil_vdev_compare, - sizeof (zil_vdev_node_t), offsetof(zil_vdev_node_t, zv_node)); - - cv_init(&zilog->zl_cv_writer, NULL, CV_DEFAULT, NULL); cv_init(&zilog->zl_cv_suspend, NULL, CV_DEFAULT, NULL); - cv_init(&zilog->zl_cv_batch[0], NULL, CV_DEFAULT, NULL); - cv_init(&zilog->zl_cv_batch[1], NULL, CV_DEFAULT, NULL); return (zilog); } @@ -1800,9 +2790,6 @@ zil_free(zilog_t *zilog) ASSERT(list_is_empty(&zilog->zl_lwb_list)); list_destroy(&zilog->zl_lwb_list); - avl_destroy(&zilog->zl_vdev_tree); - mutex_destroy(&zilog->zl_vdev_lock); - ASSERT(list_is_empty(&zilog->zl_itx_commit_list)); list_destroy(&zilog->zl_itx_commit_list); @@ -1819,12 +2806,10 @@ zil_free(zilog_t *zilog) mutex_destroy(&zilog->zl_itxg[i].itxg_lock); } + mutex_destroy(&zilog->zl_writer_lock); mutex_destroy(&zilog->zl_lock); - cv_destroy(&zilog->zl_cv_writer); cv_destroy(&zilog->zl_cv_suspend); - cv_destroy(&zilog->zl_cv_batch[0]); - cv_destroy(&zilog->zl_cv_batch[1]); kmem_free(zilog, sizeof (zilog_t)); } @@ -1837,7 +2822,8 @@ zil_open(objset_t *os, zil_get_data_t *get_data) { zilog_t *zilog = dmu_objset_zil(os); - ASSERT(zilog->zl_get_data == NULL); + ASSERT3P(zilog->zl_get_data, ==, NULL); + ASSERT3P(zilog->zl_last_lwb_opened, ==, NULL); ASSERT(list_is_empty(&zilog->zl_lwb_list)); zilog->zl_get_data = get_data; @@ -1852,22 +2838,30 @@ void zil_close(zilog_t *zilog) { lwb_t *lwb; - uint64_t txg = 0; + uint64_t txg; - zil_commit(zilog, 0); /* commit all itx */ + if (!dmu_objset_is_snapshot(zilog->zl_os)) { + zil_commit(zilog, 0); + } else { + ASSERT3P(list_tail(&zilog->zl_lwb_list), ==, NULL); + ASSERT0(zilog->zl_dirty_max_txg); + ASSERT3B(zilog_is_dirty(zilog), ==, B_FALSE); + } - /* - * The lwb_max_txg for the stubby lwb will reflect the last activity - * for the zil. After a txg_wait_synced() on the txg we know all the - * callbacks have occurred that may clean the zil. Only then can we - * destroy the zl_clean_taskq. - */ mutex_enter(&zilog->zl_lock); lwb = list_tail(&zilog->zl_lwb_list); - if (lwb != NULL) - txg = lwb->lwb_max_txg; + if (lwb == NULL) + txg = zilog->zl_dirty_max_txg; + else + txg = MAX(zilog->zl_dirty_max_txg, lwb->lwb_max_txg); mutex_exit(&zilog->zl_lock); - if (txg) + + /* + * We need to use txg_wait_synced() to wait long enough for the + * ZIL to be clean, and to wait for all pending lwbs to be + * written out. + */ + if (txg != 0) txg_wait_synced(zilog->zl_dmu_pool, txg); if (zilog_is_dirty(zilog)) @@ -1877,15 +2871,16 @@ zil_close(zilog_t *zilog) zilog->zl_get_data = NULL; /* - * We should have only one LWB left on the list; remove it now. + * We should have only one lwb left on the list; remove it now. */ mutex_enter(&zilog->zl_lock); lwb = list_head(&zilog->zl_lwb_list); if (lwb != NULL) { - ASSERT(lwb == list_tail(&zilog->zl_lwb_list)); + ASSERT3P(lwb, ==, list_tail(&zilog->zl_lwb_list)); + ASSERT3S(lwb->lwb_state, !=, LWB_STATE_ISSUED); list_remove(&zilog->zl_lwb_list, lwb); zio_buf_free(lwb->lwb_buf, lwb->lwb_sz); - kmem_cache_free(zil_lwb_cache, lwb); + zil_free_lwb(zilog, lwb); } mutex_exit(&zilog->zl_lock); } diff --git a/usr/src/uts/common/fs/zfs/zio.c b/usr/src/uts/common/fs/zfs/zio.c index f26e109267..6dbba83ee1 100644 --- a/usr/src/uts/common/fs/zfs/zio.c +++ b/usr/src/uts/common/fs/zfs/zio.c @@ -399,7 +399,7 @@ zio_add_child(zio_t *pio, zio_t *cio) * Vdev I/Os can only have vdev children. * The following ASSERT captures all of these constraints. */ - ASSERT(cio->io_child_type <= pio->io_child_type); + ASSERT3S(cio->io_child_type, <=, pio->io_child_type); zl->zl_parent = pio; zl->zl_child = cio; @@ -1106,9 +1106,9 @@ zio_flush(zio_t *zio, vdev_t *vd) void zio_shrink(zio_t *zio, uint64_t size) { - ASSERT(zio->io_executor == NULL); - ASSERT(zio->io_orig_size == zio->io_size); - ASSERT(size <= zio->io_size); + ASSERT3P(zio->io_executor, ==, NULL); + ASSERT3P(zio->io_orig_size, ==, zio->io_size); + ASSERT3U(size, <=, zio->io_size); /* * We don't shrink for raidz because of problems with the @@ -1619,8 +1619,8 @@ zio_wait(zio_t *zio) { int error; - ASSERT(zio->io_stage == ZIO_STAGE_OPEN); - ASSERT(zio->io_executor == NULL); + ASSERT3P(zio->io_stage, ==, ZIO_STAGE_OPEN); + ASSERT3P(zio->io_executor, ==, NULL); zio->io_waiter = curthread; ASSERT0(zio->io_queued_timestamp); @@ -1642,7 +1642,7 @@ zio_wait(zio_t *zio) void zio_nowait(zio_t *zio) { - ASSERT(zio->io_executor == NULL); + ASSERT3P(zio->io_executor, ==, NULL); if (zio->io_child_type == ZIO_CHILD_LOGICAL && zio_unique_parent(zio) == NULL) { @@ -1663,7 +1663,7 @@ zio_nowait(zio_t *zio) /* * ========================================================================== - * Reexecute or suspend/resume failed I/O + * Reexecute, cancel, or suspend/resume failed I/O * ========================================================================== */ @@ -1721,6 +1721,20 @@ zio_reexecute(zio_t *pio) } void +zio_cancel(zio_t *zio) +{ + /* + * Disallow cancellation of a zio that's already been issued. + */ + VERIFY3P(zio->io_executor, ==, NULL); + + zio->io_pipeline = ZIO_INTERLOCK_PIPELINE; + zio->io_done = NULL; + + zio_nowait(zio); +} + +void zio_suspend(spa_t *spa, zio_t *zio) { if (spa_get_failmode(spa) == ZIO_FAILURE_MODE_PANIC) @@ -2952,6 +2966,9 @@ zio_alloc_zil(spa_t *spa, uint64_t txg, blkptr_t *new_bp, blkptr_t *old_bp, BP_SET_LEVEL(new_bp, 0); BP_SET_DEDUP(new_bp, 0); BP_SET_BYTEORDER(new_bp, ZFS_HOST_BYTEORDER); + } else { + zfs_dbgmsg("%s: zil block allocation failure: " + "size %llu, error %d", spa_name(spa), size, error); } return (error); diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c index b724bbe5ca..2ae0d7b35b 100644 --- a/usr/src/uts/common/fs/zfs/zvol.c +++ b/usr/src/uts/common/fs/zfs/zvol.c @@ -89,6 +89,7 @@ #include <sys/dmu_tx.h> #include <sys/zfeature.h> #include <sys/zio_checksum.h> +#include <sys/zil_impl.h> #include "zfs_namecheck.h" @@ -163,7 +164,8 @@ boolean_t zvol_unmap_sync_enabled = B_FALSE; extern int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t *); static int zvol_remove_zv(zvol_state_t *); -static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio); +static int zvol_get_data(void *arg, lr_write_t *lr, char *buf, + struct lwb *lwb, zio_t *zio); static int zvol_dumpify(zvol_state_t *zv); static int zvol_dump_fini(zvol_state_t *zv); static int zvol_dump_init(zvol_state_t *zv, boolean_t resize); @@ -981,7 +983,7 @@ zvol_get_done(zgd_t *zgd, int error) zfs_range_unlock(zgd->zgd_rl); if (error == 0 && zgd->zgd_bp) - zil_add_block(zgd->zgd_zilog, zgd->zgd_bp); + zil_lwb_add_block(zgd->zgd_lwb, zgd->zgd_bp); kmem_free(zgd, sizeof (zgd_t)); } @@ -990,7 +992,7 @@ zvol_get_done(zgd_t *zgd, int error) * Get data to generate a TX_WRITE intent log record. */ static int -zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) +zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio) { zvol_state_t *zv = arg; objset_t *os = zv->zv_objset; @@ -1001,11 +1003,12 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio) zgd_t *zgd; int error; - ASSERT(zio != NULL); - ASSERT(size != 0); + ASSERT3P(lwb, !=, NULL); + ASSERT3P(zio, !=, NULL); + ASSERT3U(size, !=, 0); zgd = kmem_zalloc(sizeof (zgd_t), KM_SLEEP); - zgd->zgd_zilog = zv->zv_zilog; + zgd->zgd_lwb = lwb; zgd->zgd_rl = zfs_range_lock(&zv->zv_znode, offset, size, RL_READER); /* diff --git a/usr/src/uts/common/sys/debug.h b/usr/src/uts/common/sys/debug.h index 8efc8956b6..05e62aa1a1 100644 --- a/usr/src/uts/common/sys/debug.h +++ b/usr/src/uts/common/sys/debug.h @@ -26,7 +26,7 @@ */ /* - * Copyright (c) 2012 by Delphix. All rights reserved. + * Copyright (c) 2012, 2017 by Delphix. All rights reserved. * Copyright 2013 Saso Kiselkov. All rights reserved. */ @@ -108,17 +108,20 @@ extern void assfail3(const char *, uintmax_t, const char *, uintmax_t, __FILE__, __LINE__); \ _NOTE(CONSTCOND) } while (0) +#define VERIFY3B(x, y, z) VERIFY3_IMPL(x, y, z, boolean_t) #define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t) #define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t) #define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t) #define VERIFY0(x) VERIFY3_IMPL(x, ==, 0, uintmax_t) #if DEBUG +#define ASSERT3B(x, y, z) VERIFY3_IMPL(x, y, z, boolean_t) #define ASSERT3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t) #define ASSERT3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t) #define ASSERT3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t) #define ASSERT0(x) VERIFY3_IMPL(x, ==, 0, uintmax_t) #else +#define ASSERT3B(x, y, z) ((void)0) #define ASSERT3S(x, y, z) ((void)0) #define ASSERT3U(x, y, z) ((void)0) #define ASSERT3P(x, y, z) ((void)0) diff --git a/usr/src/uts/common/sys/time.h b/usr/src/uts/common/sys/time.h index 338338c48d..318d3898f5 100644 --- a/usr/src/uts/common/sys/time.h +++ b/usr/src/uts/common/sys/time.h @@ -248,6 +248,9 @@ struct itimerval32 { #define MSEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / MILLISEC)) #define NSEC2MSEC(n) ((n) / (NANOSEC / MILLISEC)) +#define USEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / MICROSEC)) +#define NSEC2USEC(n) ((n) / (NANOSEC / MICROSEC)) + #define NSEC2SEC(n) ((n) / (NANOSEC / SEC)) #define SEC2NSEC(m) ((hrtime_t)(m) * (NANOSEC / SEC)) diff --git a/usr/src/uts/i86pc/os/pci_bios.c b/usr/src/uts/i86pc/os/pci_bios.c index 9ee865c7d2..4c824c695a 100644 --- a/usr/src/uts/i86pc/os/pci_bios.c +++ b/usr/src/uts/i86pc/os/pci_bios.c @@ -76,6 +76,10 @@ pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp) if (nneededp) *nneededp = 0; + /* in UEFI system, there is no BIOS data */ + if (BOP_GETPROPLEN(bootops, "efi-systab") > 0) + return (0); + /* * Set up irq routing header with the size and address * of some useable low-memory data addresses. Initalize diff --git a/usr/src/uts/intel/io/acpica/acpica.c b/usr/src/uts/intel/io/acpica/acpica.c index 0fba611701..1c994e443b 100644 --- a/usr/src/uts/intel/io/acpica/acpica.c +++ b/usr/src/uts/intel/io/acpica/acpica.c @@ -253,7 +253,7 @@ acpica_check_bios_date(int yy, int mm, int dd) /* If firmware has no bios, skip the check */ if (ddi_prop_exists(DDI_DEV_T_ANY, ddi_root_node(), DDI_PROP_DONTPASS, - "bios-free")) + "efi-systab")) return (TRUE); /* |