IO-TLB: findings and debugging patch & tool
Hi,
Below please find a shell archive that contains a kernel patch & user space tool that have helped me clarify some of the IO-TLB issues.
According to my current results (obtained with a single controller), about 900 buffers obtaining ~3500 slots are needed for each disk on the 29160 with the aic7xxx driver. On my system with 4 disks, the system stays stable with 4096 8kB-pages of swiotlb space (that is, 16 times more than the default), the peak usage being 3621 buffers and 14294 2kB-slots. This peak usage was obtained with 4 separate tar jobs each writing to a different disk on the 29160. The aic7xxx driver accounts for all but 64 buffers occupying 64 slots.
The driver uses a lot more buffers than the 253 suggested by the max number of SCBs because of fragmentation of the SCSI buffers (scatter lists). Actually, the driver uses a maximum SCB count of 64 for all disks in my system. Thus, with different disks, the buffer usage could be almost 4 times higher, and limiting the max number of buffers will not solve the problem, espcecially in multiple-controller scenarios.
I would be grateful if someone could read the patch, espcecially the proc reading routine that uses several pages, since I never wrote something like that before, and i have the feeling it doesn't work 100% correctly.
I will be _very_ grateful for any bug reports and comments.
The patch is against 2.4.4/IA64 (sorry, I had no time to upgrade to 2.4.5 yet).
I put the patch and tool source together a bit in a hurry, but I have run it successfully and it has helped me quite a bit, and I will be out of office until June 25, therefore I think I should submit it today.
Regards,
Martin
Signature · 1,164 lines
Martin Wilck <[email protected]> FSC EP PS DS1, Paderborn Tel. +49 5251 8 15113 #!/bin/sh # This is a shell archive (produced by GNU sharutils 4.2.1). # To extract the files from this archive, save it to some FILE, remove # everything before the `!/bin/sh' line above, then type `sh FILE'. # # Made on 2001-06-13 23:39 CEST by <[email protected]>. # Source directory was `/home/martin'. # # Existing files will *not* be overwritten unless `-c' is specified. # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 14136 -rw-rw-r-- iotlb-mon-0.1/swiotlb.patch # 358 -rw-rw-r-- iotlb-mon-0.1/Makefile # 10494 -rw-rw-r-- iotlb-mon-0.1/iotlb-mon.c # save_IFS="${IFS}" IFS="${IFS}:" gettext_dirúILED locale_dirúILED first_param="$1" for dir in $PATH do if test "$gettext_dir" = FAILED && test -f $dir/gettext \ && ($dir/gettext --version >/dev/null 2>&1) then set `$dir/gettext --version 2>&1` if test "$3" = GNU then gettext_dir=$dir fi fi if test "$locale_dir" = FAILED && test -f $dir/shar \ && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) then locale_dir=`$dir/shar --print-text-domain-dir` fi done IFS="$save_IFS" if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED then echoìho else TEXTDOMAINDIR=$locale_dir export TEXTDOMAINDIR TEXTDOMAIN=sharutils export TEXTDOMAIN echo="$gettext_dir/gettext -s" fi if touch -am -t 200112312359.59 $$.touch >/dev/null 2>&1 && test ! -f 200112312359.59 -a -f $$.touch; then shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"' elif touch -am 123123592001.59 $$.touch >/dev/null 2>&1 && test ! -f 123123592001.59 -a ! -f 123123592001.5 -a -f $$.touch; then shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"' elif touch -am 1231235901 $$.touch >/dev/null 2>&1 && test ! -f 1231235901 -a -f $$.touch; then shar_touch='touch -am $3$4$5$6$2 "$8"' else shar_touch=: echo $echo 'WARNING: not restoring timestamps. Consider getting and' $echo "installing GNU \`touch', distributed in GNU File Utilities..." echo fi rm -f 200112312359.59 123123592001.59 123123592001.5 1231235901 $$.touch # if mkdir _sh05939; then $echo 'x -' 'creating lock directory' else $echo 'failed to create lock directory' exit 1 fi # ======= iotlb-mon-0.1/swiotlb.patch ======= if test ! -d 'iotlb-mon-0.1'; then $echo 'x -' 'creating directory' 'iotlb-mon-0.1' mkdir 'iotlb-mon-0.1' fi if test -f 'iotlb-mon-0.1/swiotlb.patch' && test "$first_param" != -c; then $echo 'x -' SKIPPING 'iotlb-mon-0.1/swiotlb.patch' '(file already exists)' else $echo 'x -' extracting 'iotlb-mon-0.1/swiotlb.patch' '(text)' sed 's/^X//' << 'SHAR_EOF' > 'iotlb-mon-0.1/swiotlb.patch' && --- linux-2.4.4-orig/Documentation/Configure.help Wed May 30 20:31:31 2001 +++ linux-2.4.4mw/Documentation/Configure.help Wed Jun 13 23:23:53 2001 @@ -17670,6 +17670,21 @@ X To use this option, you have to check that the "/proc file system X support" (CONFIG_PROC_FS) is enabled, too. X +/proc/swiotlb support +CONFIG_PROCFS_SWIOTLB + If you say Y here, you will find information on the software + IO-TLB usage in your machine in /proc/swiotlb. + + IO-TLBs (Translation Lookaside Buffers) are needed if PCI devices + unable to do 64-bit addressing are used in machines with large RAM. + This is mainly useful for debugging purposes on machines with + >= 4GB RAM. + + To use this option, you have to check that the "/proc file system + support" (CONFIG_PROC_FS) is enabled, too. + + If unsure, say N. + X # X # A couple of things I keep forgetting: X # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, --- linux-2.4.4-orig/arch/ia64/config.in Wed May 30 17:37:23 2001 +++ linux-2.4.4mw/arch/ia64/config.in Tue Jun 12 19:41:51 2001 @@ -111,6 +111,7 @@ X bool 'Performance monitor support' CONFIG_PERFMON X tristate '/proc/pal support' CONFIG_IA64_PALINFO X tristate '/proc/efi/vars support' CONFIG_EFI_VARS +tristate '/proc/swiotlb support' CONFIG_PROCFS_SWIOTLB X X bool 'Networking support' CONFIG_NET X bool 'System V IPC' CONFIG_SYSVIPC --- linux-2.4.4-orig/arch/ia64/lib/swiotlb.c Wed May 30 17:36:58 2001 +++ linux-2.4.4mw/arch/ia64/lib/swiotlb.c Wed Jun 13 10:48:23 2001 @@ -24,6 +24,13 @@ X #include <linux/init.h> X #include <linux/bootmem.h> X +#if defined CONFIG_PROCFS_SWIOTLB || defined CONFIG_PROCFS_SWIOTLB_MODULE +# define EXPORT_IOTLB_SYMS 1 +# define IOTLB_STATIC +#else +# define IOTLB_STATIC static +#endif + X #define ALIGN(val, align) ((unsigned long) \ X (((unsigned long) (val) + ((align) - 1)) & ~((align) - 1))) X @@ -36,19 +43,19 @@ X * Used to do a quick range check in swiotlb_unmap_single and swiotlb_sync_single, to see X * if the memory was in fact allocated by this API. X */ -static char *io_tlb_start, *io_tlb_end; +IOTLB_STATIC char *io_tlb_start, *io_tlb_end; X X /* X * The number of IO TLB blocks (in groups of 64) betweeen io_tlb_start and io_tlb_end. X * This is command line adjustable via setup_io_tlb_npages. X */ -static unsigned long io_tlb_nslabs = 1024; +IOTLB_STATIC unsigned long io_tlb_nslabs = 1024; X X /* X * This is a free list describing the number of free entries available from each index X */ -static unsigned int *io_tlb_list; -static unsigned int io_tlb_index; +IOTLB_STATIC unsigned int *io_tlb_list; +IOTLB_STATIC unsigned int io_tlb_index; X X /* X * We need to save away the original address corresponding to a mapped entry for the sync @@ -56,6 +63,16 @@ X */ X static unsigned char **io_tlb_orig_addr; X +#ifdef EXPORT_IOTLB_SYMS +/* + * Usage data for /proc entries + */ +unsigned int io_tlb_used_buffers = 0; +unsigned int io_tlb_used_slots = 0; +unsigned int io_tlb_max_used_buffers = 0; +unsigned int io_tlb_max_used_slots = 0; +#endif + X /* X * Protect the above data structures in the map and unmap calls X */ @@ -173,6 +190,15 @@ X found: X spin_unlock_irqrestore(&io_tlb_lock, flags); X +#ifdef EXPORT_IOTLB_SYMS + io_tlb_used_slots += nslots; + io_tlb_used_buffers++; + if (io_tlb_used_slots > io_tlb_max_used_slots) + io_tlb_max_used_slots = io_tlb_used_slots; + if (io_tlb_used_buffers > io_tlb_max_used_buffers) + io_tlb_max_used_buffers = io_tlb_used_buffers; +#endif + X /* X * Save away the mapping from the original address to the DMA address. This is X * needed when we sync the memory. Then we sync the buffer if needed. @@ -181,6 +207,7 @@ X if (direction = PCI_DMA_TODEVICE || direction = PCI_DMA_BIDIRECTIONAL) X memcpy(dma_addr, buffer, size); X + printk ("+%1d", nslots); X return dma_addr; X } X @@ -228,6 +255,11 @@ X io_tlb_list[i] = ++count; X } X spin_unlock_irqrestore(&io_tlb_lock, flags); +#ifdef EXPORT_IOTLB_SYMS + io_tlb_used_buffers--; + io_tlb_used_slots -= nslots; +#endif + printk ("-%1d", nslots); X } X X static void @@ -462,3 +494,15 @@ X EXPORT_SYMBOL(swiotlb_dma_address); X EXPORT_SYMBOL(swiotlb_alloc_consistent); X EXPORT_SYMBOL(swiotlb_free_consistent); + +#ifdef EXPORT_IOTLB_SYMS +EXPORT_SYMBOL(io_tlb_start); +EXPORT_SYMBOL(io_tlb_end); +EXPORT_SYMBOL(io_tlb_nslabs); +EXPORT_SYMBOL(io_tlb_index); +EXPORT_SYMBOL(io_tlb_list); +EXPORT_SYMBOL(io_tlb_used_buffers); +EXPORT_SYMBOL(io_tlb_used_slots); +EXPORT_SYMBOL(io_tlb_max_used_buffers); +EXPORT_SYMBOL(io_tlb_max_used_slots); +#endif /* EXPORT_IOTLB_SYMS */ a0 175 #include <linux/module.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/proc_fs.h> X MODULE_AUTHOR("Martin Wilck <[email protected]>"); MODULE_DESCRIPTION("/proc interface for software IO/TLB status"); X struct iotlb_info_struct { X unsigned long n_slots; X unsigned int index; X unsigned int used_buffers; X unsigned int used_slots; X unsigned int max_used_buffers; X unsigned int max_used_slots; X void *start; X void *end; }; X /* Variables exported from arch/ia64/lib/swiotlb.c */ extern char *io_tlb_start, *io_tlb_end; extern unsigned long io_tlb_nslabs; extern unsigned int *io_tlb_list; extern unsigned int io_tlb_index; extern unsigned int io_tlb_used_buffers; extern unsigned int io_tlb_used_slots; extern unsigned int io_tlb_max_used_buffers; extern unsigned int io_tlb_max_used_slots; X static struct proc_dir_entry *swiotlb_proc_dir; static struct proc_dir_entry *proc_iotlb_list; static struct proc_dir_entry *proc_iotlb_params; static struct proc_dir_entry *proc_iotlb_parameters; X static char *swiotlb_outbuf = NULL; X static int read_iotlb_list(char *page, char **start, off_t offset, int count, int *eof, void *data) { X int nbytes = io_tlb_nslabs * sizeof (int); X int kbytes = count + offset; X char *buf; X int cnt; X X MOD_INC_USE_COUNT; X if (nbytes > PAGE_SIZE) { X if (!swiotlb_outbuf) { X swiotlb_outbuf = kmalloc (io_tlb_nslabs * sizeof (int), GFP_KERNEL); X if (!swiotlb_outbuf) { X cnt = -ENOMEM; X goto exit; X }; X }; X *start = buf = swiotlb_outbuf; X } else { X buf = page; X }; X X if (kbytes < nbytes) X cnt = kbytes; X else { X cnt = nbytes; X *eof = 1; X }; X X memcpy (buf, io_tlb_list, cnt); X X cnt -= offset; X cnt = (cnt < 0 ? 0 : cnt); X X exit: X MOD_DEC_USE_COUNT; X return cnt; X } X static int read_iotlb_params(char *page, char **start, off_t offset, int count, int *eof, void *data) { X struct iotlb_info_struct *iotlb_params X (struct iotlb_info_struct*) page; X const int nbytes = sizeof (struct iotlb_info_struct); X int res; X X MOD_INC_USE_COUNT; X X iotlb_params->n_slots = io_tlb_nslabs; X iotlb_params->index = io_tlb_index; X iotlb_params->used_buffers = io_tlb_used_buffers; X iotlb_params->used_slots = io_tlb_used_slots; X iotlb_params->max_used_buffers = io_tlb_max_used_buffers; X iotlb_params->max_used_slots = io_tlb_max_used_slots; X iotlb_params->start = io_tlb_start; X iotlb_params->end = io_tlb_end; X X if (offset + count >= nbytes) { X *eof = 1; X res = (nbytes - offset < 0 ? 0 : nbytes - offset); X } else { X res = count; X }; X X MOD_DEC_USE_COUNT; X return res; } X static int read_iotlb_parameters(char *page, char **start, off_t offset, int count, int *eof, void *data) { X int len, res; X MOD_INC_USE_COUNT; X len = sprintf (page, X "Slots allocated : %8ld\n" X "Current Index : %8d\n" X "Buffers used : %8d\n" X "Slots used : %8d\n" X "Max Buffers used : %8d\n" X "Max Slots used : %8d\n" X "Start address : 0x%p\n" X "End address : 0x%p\n", X io_tlb_nslabs, X io_tlb_index, X io_tlb_used_buffers, X io_tlb_used_slots, X io_tlb_max_used_buffers, X io_tlb_max_used_slots, X (void*) io_tlb_start, X (void*) io_tlb_end); X if (offset + count >= len) { X *eof = 1; X len -= offset; X res = (len < 0 ? 0 : len); X } else X res = count; X MOD_DEC_USE_COUNT; X return res; } X static int __init swiotlb_init_proc (void) { X printk (KERN_INFO "Loading /proc/swiotlb support\n"); X X swiotlb_proc_dir = proc_mkdir ("swiotlb", NULL); X proc_iotlb_list X create_proc_entry ("list", 0444, swiotlb_proc_dir); X proc_iotlb_params X create_proc_entry ("params", 0444, swiotlb_proc_dir); X proc_iotlb_parameters X create_proc_entry ("parameters", 0444, swiotlb_proc_dir); X X proc_iotlb_list->read_proc = read_iotlb_list; X proc_iotlb_params->read_proc = read_iotlb_params; X proc_iotlb_parameters->read_proc = read_iotlb_parameters; X X return 0; } X X static void __exit swiotlb_exit_proc (void) { X printk (KERN_INFO "Unloading /proc/swiotlb support\n"); X if (swiotlb_outbuf) X kfree (swiotlb_outbuf); X remove_proc_entry ("list", swiotlb_proc_dir); X remove_proc_entry ("params", swiotlb_proc_dir); X remove_proc_entry ("parameters", swiotlb_proc_dir); X remove_proc_entry ("swiotlb", 0); } X module_init(swiotlb_init_proc); module_exit(swiotlb_exit_proc); --- linux-2.4.4-orig/arch/ia64/kernel/swiotlb_proc.c Wed Jun 13 23:28:08 2001 +++ linux-2.4.4mw/arch/ia64/kernel/swiotlb_proc.c Wed Jun 13 11:00:31 2001 @@ -0,0 +1,175 @@ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/proc_fs.h> + +MODULE_AUTHOR("Martin Wilck <[email protected]>"); +MODULE_DESCRIPTION("/proc interface for software IO/TLB status"); + +struct iotlb_info_struct { + unsigned long n_slots; + unsigned int index; + unsigned int used_buffers; + unsigned int used_slots; + unsigned int max_used_buffers; + unsigned int max_used_slots; + void *start; + void *end; +}; + +/* Variables exported from arch/ia64/lib/swiotlb.c */ +extern char *io_tlb_start, *io_tlb_end; +extern unsigned long io_tlb_nslabs; +extern unsigned int *io_tlb_list; +extern unsigned int io_tlb_index; +extern unsigned int io_tlb_used_buffers; +extern unsigned int io_tlb_used_slots; +extern unsigned int io_tlb_max_used_buffers; +extern unsigned int io_tlb_max_used_slots; + +static struct proc_dir_entry *swiotlb_proc_dir; +static struct proc_dir_entry *proc_iotlb_list; +static struct proc_dir_entry *proc_iotlb_params; +static struct proc_dir_entry *proc_iotlb_parameters; + +static char *swiotlb_outbuf = NULL; + +static int +read_iotlb_list(char *page, char **start, off_t offset, int count, int *eof, void *data) +{ + int nbytes = io_tlb_nslabs * sizeof (int); + int kbytes = count + offset; + char *buf; + int cnt; + + MOD_INC_USE_COUNT; + if (nbytes > PAGE_SIZE) { + if (!swiotlb_outbuf) { + swiotlb_outbuf = kmalloc (io_tlb_nslabs * sizeof (int), GFP_KERNEL); + if (!swiotlb_outbuf) { + cnt = -ENOMEM; + goto exit; + }; + }; + *start = buf = swiotlb_outbuf; + } else { + buf = page; + }; + + if (kbytes < nbytes) + cnt = kbytes; + else { + cnt = nbytes; + *eof = 1; + }; + + memcpy (buf, io_tlb_list, cnt); + + cnt -= offset; + cnt = (cnt < 0 ? 0 : cnt); + + exit: + MOD_DEC_USE_COUNT; + return cnt; + +} + +static int +read_iotlb_params(char *page, char **start, off_t offset, int count, int *eof, void *data) +{ + struct iotlb_info_struct *iotlb_params + (struct iotlb_info_struct*) page; + const int nbytes = sizeof (struct iotlb_info_struct); + int res; + + MOD_INC_USE_COUNT; + + iotlb_params->n_slots = io_tlb_nslabs; + iotlb_params->index = io_tlb_index; + iotlb_params->used_buffers = io_tlb_used_buffers; + iotlb_params->used_slots = io_tlb_used_slots; + iotlb_params->max_used_buffers = io_tlb_max_used_buffers; + iotlb_params->max_used_slots = io_tlb_max_used_slots; + iotlb_params->start = io_tlb_start; + iotlb_params->end = io_tlb_end; + + if (offset + count >= nbytes) { + *eof = 1; + res = (nbytes - offset < 0 ? 0 : nbytes - offset); + } else { + res = count; + }; + + MOD_DEC_USE_COUNT; + return res; +} + +static int +read_iotlb_parameters(char *page, char **start, off_t offset, int count, int *eof, void *data) +{ + int len, res; + MOD_INC_USE_COUNT; + len = sprintf (page, + "Slots allocated : %8ld\n" + "Current Index : %8d\n" + "Buffers used : %8d\n" + "Slots used : %8d\n" + "Max Buffers used : %8d\n" + "Max Slots used : %8d\n" + "Start address : 0x%p\n" + "End address : 0x%p\n", + io_tlb_nslabs, + io_tlb_index, + io_tlb_used_buffers, + io_tlb_used_slots, + io_tlb_max_used_buffers, + io_tlb_max_used_slots, + (void*) io_tlb_start, + (void*) io_tlb_end); + if (offset + count >= len) { + *eof = 1; + len -= offset; + res = (len < 0 ? 0 : len); + } else + res = count; + MOD_DEC_USE_COUNT; + return res; +} + +static int __init +swiotlb_init_proc (void) +{ + printk (KERN_INFO "Loading /proc/swiotlb support\n"); + + swiotlb_proc_dir = proc_mkdir ("swiotlb", NULL); + proc_iotlb_list + create_proc_entry ("list", 0444, swiotlb_proc_dir); + proc_iotlb_params + create_proc_entry ("params", 0444, swiotlb_proc_dir); + proc_iotlb_parameters + create_proc_entry ("parameters", 0444, swiotlb_proc_dir); + + proc_iotlb_list->read_proc = read_iotlb_list; + proc_iotlb_params->read_proc = read_iotlb_params; + proc_iotlb_parameters->read_proc = read_iotlb_parameters; + + return 0; +} + + +static void __exit +swiotlb_exit_proc (void) +{ + printk (KERN_INFO "Unloading /proc/swiotlb support\n"); + if (swiotlb_outbuf) + kfree (swiotlb_outbuf); + remove_proc_entry ("list", swiotlb_proc_dir); + remove_proc_entry ("params", swiotlb_proc_dir); + remove_proc_entry ("parameters", swiotlb_proc_dir); + remove_proc_entry ("swiotlb", 0); +} + +module_init(swiotlb_init_proc); +module_exit(swiotlb_exit_proc); --- linux-2.4.4-orig/arch/ia64/kernel/Makefile Wed May 30 17:37:23 2001 +++ linux-2.4.4mw/arch/ia64/kernel/Makefile Tue Jun 12 20:05:49 2001 @@ -20,6 +20,7 @@ X obj-$(CONFIG_IA64_DIG) += iosapic.o X obj-$(CONFIG_IA64_PALINFO) += palinfo.o X obj-$(CONFIG_EFI_VARS) += efivars.o +obj-$(CONFIG_PROCFS_SWIOTLB) += swiotlb_proc.o X obj-$(CONFIG_PCI) += pci.o X obj-$(CONFIG_SMP) += smp.o smpboot.o X obj-$(CONFIG_IA64_MCA) += mca.o mca_asm.o SHAR_EOF (set 20 01 06 13 23 31 13 'iotlb-mon-0.1/swiotlb.patch'; eval "$shar_touch") && chmod 0664 'iotlb-mon-0.1/swiotlb.patch' || $echo 'restore of' 'iotlb-mon-0.1/swiotlb.patch' 'failed' if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then md5sum -c << SHAR_EOF >/dev/null 2>&1 \ || $echo 'iotlb-mon-0.1/swiotlb.patch:' 'MD5 check failed' ffe958de5009d238c1eee4277bb17e93 iotlb-mon-0.1/swiotlb.patch SHAR_EOF else shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'iotlb-mon-0.1/swiotlb.patch'`" test 14136 -eq "$shar_count" || $echo 'iotlb-mon-0.1/swiotlb.patch:' 'original size' '14136,' 'current size' "$shar_count!" fi fi # ======= iotlb-mon-0.1/Makefile ======= if test -f 'iotlb-mon-0.1/Makefile' && test "$first_param" != -c; then $echo 'x -' SKIPPING 'iotlb-mon-0.1/Makefile' '(file already exists)' else $echo 'x -' extracting 'iotlb-mon-0.1/Makefile' '(text)' sed 's/^X//' << 'SHAR_EOF' > 'iotlb-mon-0.1/Makefile' && CFLAGS = -O2 -Wall LDFLAGS = -s PREFIX = /usr/local X OBJ = iotlb-mon.o LIBS = -lncurses EXE = iotlb-mon X sbindir = $(PREFIX)/sbin X default: $(EXE) X $(EXE): $(OBJ) X $(CC) $(CFLAGS) $(LDFLAGS) -o $(EXE) $(OBJ) $(LIBS) X install: $(EXE) X install -d $(sbindir) X install -m 755 $(EXE) $(sbindir) X clean: X rm -f $(EXE) $(OBJ) core X distclean: clean X rm -f *~ '#'* SHAR_EOF (set 20 01 06 13 23 08 50 'iotlb-mon-0.1/Makefile'; eval "$shar_touch") && chmod 0664 'iotlb-mon-0.1/Makefile' || $echo 'restore of' 'iotlb-mon-0.1/Makefile' 'failed' if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then md5sum -c << SHAR_EOF >/dev/null 2>&1 \ || $echo 'iotlb-mon-0.1/Makefile:' 'MD5 check failed' 54357a32a53e5ade6918ff7b409a31a0 iotlb-mon-0.1/Makefile SHAR_EOF else shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'iotlb-mon-0.1/Makefile'`" test 358 -eq "$shar_count" || $echo 'iotlb-mon-0.1/Makefile:' 'original size' '358,' 'current size' "$shar_count!" fi fi # ======= iotlb-mon-0.1/iotlb-mon.c ======= if test -f 'iotlb-mon-0.1/iotlb-mon.c' && test "$first_param" != -c; then $echo 'x -' SKIPPING 'iotlb-mon-0.1/iotlb-mon.c' '(file already exists)' else $echo 'x -' extracting 'iotlb-mon-0.1/iotlb-mon.c' '(text)' sed 's/^X//' << 'SHAR_EOF' > 'iotlb-mon-0.1/iotlb-mon.c' && /* X X iotlb-mon.c - utility to monitor software IO-TLB usage on X IA-64 Linux systems. X X (c) 2001 Martin Wilck <[email protected]> X X Copying and modification granted under the terms of the X GNU General Public License (GPL). X X ************************************************************* X This program comes with NO WARRANTY, for details see the GPL. X ************************************************************* X X Version 0.1, 13.06.2001 X X Compiling iotlb-mon X =========X X Requires the ncurses library and header files. X X make; make PREFIX=[my_install_root] install X X Default for PREFIX is /usr/local. X The program installs to $(PREFIX)/sbin. X X Using iotlb-mon X =======X X usage: ioltb-mon X X The kernel module "swiotlb_proc" must be loaded for this X program to work. X X The program has two modes: X -------------------------- X X * Info mode: A IO-TLB usage summary is displayed. X * List mode: A map of the IO-TLB list is displayed. Each entry X in this list represents the number of free IO-TLB slots after X the current entry, including the entry itself. X X The entries are displayed as follows: X '0' - this entry occupied X '1' - this entry free, next entry occupied, X '2' - this entry & next entry free, ..., X '9' - this entry & next 8 entries free, X 'a' - this entry & next 9 entries free, ..., X 'A' - this entry & next 35 entries free, ..., X 'Z' - this entry & next 60 entries free, ..., X '-' - this entry and more than 60 following entries free. X X Keyboard controls: X ------------------ X X q, x: quit X l : switch to list mode X i : switch to info mode X + : increase timer interval (0.1 - 1 s) X - : decrease timer interval X X The following keys apply only to list mode: X X n : move displayed window downward in the list ("next") X p : move displayed window upward in the list ("previous") X < : move displayed window to the beginning of the list X > : move displayed window to the end of the list X f : Toggle "follow index" mode - in that mode, the window will X always follow the current slot index, so that the above X keyboard controls may not have the desired effect. X X BUGS: X ----- X X Probably many. X X I don't believe the list-mode display yet, although it's X not complete junk. X It shows weird periodicities that may be due to X a bug in the read code of the swiotlb_proc kernel module. X X Please report bugs and suggest improvements to X <[email protected]>. X X */ X #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <curses.h> #include <sys/time.h> X #define N_INFO_FIELDS 8 #define PWIN_BORDER 1 #define INFO_X PWIN_BORDER #define INFO_Y (PWIN_BORDER) #define PWIN_HEIGHT (N_INFO_FIELDS+INFO_Y+PWIN_BORDER) #define INFO_SPC 2 #define INFO_FLEN 18 #define INFO_UFMT "%18u" #define INFO_PFMT "%18p" #define PWIN_X 2 #define PWIN_Y 2 #define MIN_INTERVAL 1 #define MAX_INTERVAL 10 #define IWIN_HEIGHT (LINES - 1) #define INDEX_STEP 1024 X typedef enum { X QUIT = 0, X PARAMS, X LIST } run_t; X struct iotlb_info_struct { X unsigned long n_slots; X unsigned int index; X unsigned int used_buffers; X unsigned int used_slots; X unsigned int max_used_buffers; X unsigned int max_used_slots; X void *start; X void *end; }; X static const char *params_name = "/proc/swiotlb/params"; static const char *list_name = "/proc/swiotlb/list"; static const char *info_desc [N_INFO_FIELDS] = { X "Allocated", X "Index", X "Used buffers", X "Max used buffers", X "Used slots", X "Max used slots", X "Start address", X "End address" }; X static unsigned int first_index = 0; static bool follow_index = FALSE; static int iotlb_fd, list_fd; static int interval = 5; static WINDOW *mainwin, *pwin, *cwin, *iwin; volatile sig_atomic_t alarm_received = 0; X void cleanup () { X endwin (); X close (iotlb_fd); X close (list_fd); } X static int info_len (int force) { X static int maxl = 0; X int i; X X if (maxl && !force) X return maxl; X X maxl = 0; X for (i = 0; i < N_INFO_FIELDS; i++) { X int l = strlen (info_desc[i]); X if (l > maxl) X maxl = l; X }; X maxl++; X return maxl; } X static void create_windows (void) { X int width = info_len(0) + INFO_SPC + INFO_FLEN + 2*PWIN_BORDER; X X pwin = newwin (PWIN_HEIGHT, X width, X PWIN_Y, X (COLS - width) >> 1); X cwin = newwin (1, 0, LINES - 1, 0); X iwin = newwin (IWIN_HEIGHT, 0, 0, 0); X X if (!pwin || !cwin || !iwin) { X cleanup (); X fprintf (stderr, "Error creating windows"); X exit (1); X }; X box (pwin, 0, 0); } X static int print_info_desc (int force) { X X int maxl = info_len (force), i; X mvwprintw (pwin, 0, INFO_X, X "Software IO-TLB status"); X for (i = 0; i < N_INFO_FIELDS; i++) { X mvwprintw (pwin, INFO_Y + i, INFO_X, X "%s", info_desc[i]); X mvwaddch (pwin, INFO_Y + i, INFO_X + maxl, ':'); X }; X X return maxl; } X static int read_info (struct iotlb_info_struct *inf) { int nread; X int st; X X st = lseek (iotlb_fd, 0, SEEK_SET); X X nread = read (iotlb_fd, inf, sizeof (struct iotlb_info_struct)); X if (nread != sizeof (struct iotlb_info_struct)) { X cleanup (); X fprintf (stderr, "error in read () on %s\n", X params_name); X exit (1); X }; X X return nread; } X static void print_info (void) { X struct iotlb_info_struct info; X int nread, col; X X nread = read_info (&info); X col = print_info_desc (0) + INFO_X + INFO_SPC; X X mvwprintw (pwin, INFO_Y + 0, col, INFO_UFMT, info.n_slots); X mvwprintw (pwin, INFO_Y + 1, col, INFO_UFMT, info.index); X mvwprintw (pwin, INFO_Y + 2, col, INFO_UFMT, info.used_buffers); X mvwprintw (pwin, INFO_Y + 3, col, INFO_UFMT, info.max_used_buffers); X mvwprintw (pwin, INFO_Y + 4, col, INFO_UFMT, info.used_slots); X mvwprintw (pwin, INFO_Y + 5, col, INFO_UFMT, info.max_used_slots); X mvwprintw (pwin, INFO_Y + 6, col, INFO_PFMT, info.start); X mvwprintw (pwin, INFO_Y + 7, col, INFO_PFMT, info.end); X wrefresh (pwin); } X static int read_list (unsigned int **lst, struct iotlb_info_struct *info) { X static int size = 0; X static unsigned int *list = NULL; X int nread, st, n, i; X char *pos; X X nread = read_info (info); X if (!list) { X size = info->n_slots; X if (size > 0) { X list = malloc (size * sizeof (unsigned int)); X }; X if (!list) { X cleanup (); X fprintf (stderr, X "Error in malloc ()\n"); X exit (1); X }; X for (i = 0; i < size; i++) X list[i] = 1; X }; X X st = lseek (list_fd, 0, SEEK_SET); X X pos = (char*) list; X n = size * sizeof (unsigned int); X do { X nread = read (list_fd, pos, n); /* pos += nread; */ X n -= nread; X } while (0); /* (nread > 0); */ X X if (nread < 0) { X cleanup (); X fprintf (stderr, "error in read () on %s: %d\n", X list_name, nread); X exit (1); X }; X X *lst = list; X return size - n; } X static chtype make_ch (unsigned int v) { X chtype c; X if (v < 10u) X c = '0' + v; X else if (v < 36u) X c = 'a' + v - 10; X else if (v < 62u) X c = 'A' + v - 36; X else X c = '-'; X return c; } X static void print_list (void) { X unsigned int *list, index; X int sz, mx, i, last; X struct iotlb_info_struct info; X X sz = read_list (&list, &info); X index = info.index; X mx = COLS * IWIN_HEIGHT; X X if (first_index >= sz) { X first_index = (sz - 1) & ~(INDEX_STEP - 1); X }; X if (follow_index && X (first_index > index || index - first_index > mx)) { X first_index = index & ~(INDEX_STEP - 1); X }; X X last = first_index + mx; X if (last > sz) X last = sz; X X mvwprintw (iwin, 0, 0, "IO-TLB list slot %8u - %8u. " X "read %d, index %8u, used %8u", X first_index, last, sz, index, info.used_slots); X X wmove (iwin, 1, 0); X for (i = first_index; i < last; ++i) { X if (i = index) { X wattron (iwin, A_REVERSE); X waddch (iwin, make_ch (list[i])); X wattroff (iwin, A_REVERSE); X } else if (i % 1024 = 0) { X wattron (iwin, A_UNDERLINE); X waddch (iwin, make_ch (list[i])); X wattroff (iwin, A_UNDERLINE); X } else X waddch (iwin, make_ch (list[i])); X }; X for (i = last; i < first_index + mx; i++) X waddch (iwin, ' '); X wrefresh (iwin); } X void alrm_handler (int sig) { X alarm_received = 1; } X int start_timer (long int decisec) { X static const struct itimerval itv0 = { X { 0, 0}, { 0, 0} X }; X struct itimerval itv; X int st; X long int sec = decisec / 10; X long int usec = 100000 * (decisec % 10); X struct sigaction act; X X act.sa_handler = alrm_handler; X sigemptyset (&act.sa_mask); X sigaction (SIGALRM, &act, NULL); X X itv.it_interval.tv_sec = sec; X itv.it_interval.tv_usec = usec; X itv.it_value.tv_sec = sec; X itv.it_value.tv_usec = usec; X X setitimer (ITIMER_REAL, &itv0, NULL); X st = setitimer (ITIMER_REAL, &itv, NULL); X X mvwprintw (cwin, 0, 0, "Timer interval (1/10 s): %3d", decisec); X return st; } X run_t handle_input (run_t run) { X chtype ch; X ch = wgetch(cwin); X if (ch != ERR) { X switch (ch) { X case '+': X if (interval < MAX_INTERVAL) { X interval++; X start_timer (interval); X }; X break; X case '-': X if (interval > MIN_INTERVAL) { X interval--; X start_timer (interval); X }; X break; X case 'i': X run = PARAMS; X werase (iwin); X wrefresh (iwin); X box (pwin, 0, 0); X break; X case 'l': X run = LIST; X werase (pwin); X wrefresh (pwin); X break; X case 'q': X case 'x': X run = QUIT; X break; X case 'f': X follow_index = ~follow_index & 1; X if (follow_index) X mvwprintw (cwin, 0, COLS - 20, X "Follow Index ON"); X else X mvwprintw (cwin, 0, COLS - 20, X "Follow Index OFF"); X break; X case '<': X first_index = 0; X break; X case '>': X first_index = 1 << 24; X break; X case 'n': X first_index += INDEX_STEP; X break; X case 'p': X first_index -= INDEX_STEP; X break; X default: X break; X }; X wrefresh(cwin); X }; X X return run; } X int main (int argc, char **argv) { X sigset_t alrm_mask; X run_t run = PARAMS; X X iotlb_fd = open (params_name, O_RDONLY, 0); X list_fd = open (list_name, O_RDONLY, 0); X if (iotlb_fd < 0 || list_fd < 0) X perror ("open"); X X mainwin = initscr (); X cbreak (); X noecho (); X X create_windows (); X X sigemptyset (&alrm_mask); X sigaddset (&alrm_mask, SIGALRM); X X start_timer (interval); X X while (run != QUIT) { X run = handle_input (run); X sigprocmask (SIG_BLOCK, &alrm_mask, NULL); X if (alarm_received) { X alarm_received = 0; X switch (run) { X case PARAMS: X print_info (); X break; X case LIST: X print_list (); X break; X default: X break; X }; X }; X sigprocmask (SIG_UNBLOCK, &alrm_mask, NULL); X }; X X cleanup (); X return 0; } SHAR_EOF (set 20 01 06 13 23 06 48 'iotlb-mon-0.1/iotlb-mon.c'; eval "$shar_touch") && chmod 0664 'iotlb-mon-0.1/iotlb-mon.c' || $echo 'restore of' 'iotlb-mon-0.1/iotlb-mon.c' 'failed' if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then md5sum -c << SHAR_EOF >/dev/null 2>&1 \ || $echo 'iotlb-mon-0.1/iotlb-mon.c:' 'MD5 check failed' 61f5d6a6e6d9a5e2d0fcf56d5caea078 iotlb-mon-0.1/iotlb-mon.c SHAR_EOF else shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'iotlb-mon-0.1/iotlb-mon.c'`" test 10494 -eq "$shar_count" || $echo 'iotlb-mon-0.1/iotlb-mon.c:' 'original size' '10494,' 'current size' "$shar_count!" fi fi rm -fr _sh05939 exit 0