diff options
43 files changed, 1185 insertions, 238 deletions
diff --git a/.gitignore b/.gitignore index 9ac91060ea64..acb6afe6b7a3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ Module.symvers /TAGS /linux /vmlinux +/vmlinux-gdb.py /vmlinuz /System.map /Module.markers diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt new file mode 100644 index 000000000000..7050ce8794b9 --- /dev/null +++ b/Documentation/gdb-kernel-debugging.txt @@ -0,0 +1,160 @@ +Debugging kernel and modules via gdb +==================================== + +The kernel debugger kgdb, hypervisors like QEMU or JTAG-based hardware +interfaces allow to debug the Linux kernel and its modules during runtime +using gdb. Gdb comes with a powerful scripting interface for python. The +kernel provides a collection of helper scripts that can simplify typical +kernel debugging steps. This is a short tutorial about how to enable and use +them. It focuses on QEMU/KVM virtual machines as target, but the examples can +be transferred to the other gdb stubs as well. + + +Requirements +------------ + + o gdb 7.2+ (recommended: 7.4+) with python support enabled (typically true + for distributions) + + +Setup +----- + + o Create a virtual Linux machine for QEMU/KVM (see www.linux-kvm.org and + www.qemu.org for more details). For cross-development, + http://landley.net/aboriginal/bin keeps a pool of machine images and + toolchains that can be helpful to start from. + + o Build the kernel with CONFIG_GDB_SCRIPTS enabled, but leave + CONFIG_DEBUG_INFO_REDUCED off. If your architecture supports + CONFIG_FRAME_POINTER, keep it enabled. + + o Install that kernel on the guest. + + Alternatively, QEMU allows to boot the kernel directly using -kernel, + -append, -initrd command line switches. This is generally only useful if + you do not depend on modules. See QEMU documentation for more details on + this mode. + + o Enable the gdb stub of QEMU/KVM, either + - at VM startup time by appending "-s" to the QEMU command line + or + - during runtime by issuing "gdbserver" from the QEMU monitor + console + + o cd /path/to/linux-build + + o Start gdb: gdb vmlinux + + Note: Some distros may restrict auto-loading of gdb scripts to known safe + directories. In case gdb reports to refuse loading vmlinux-gdb.py, add + + add-auto-load-safe-path /path/to/linux-build + + to ~/.gdbinit. See gdb help for more details. + + o Attach to the booted guest: + (gdb) target remote :1234 + + +Examples of using the Linux-provided gdb helpers +------------------------------------------------ + + o Load module (and main kernel) symbols: + (gdb) lx-symbols + loading vmlinux + scanning for modules in /home/user/linux/build + loading @0xffffffffa0020000: /home/user/linux/build/net/netfilter/xt_tcpudp.ko + loading @0xffffffffa0016000: /home/user/linux/build/net/netfilter/xt_pkttype.ko + loading @0xffffffffa0002000: /home/user/linux/build/net/netfilter/xt_limit.ko + loading @0xffffffffa00ca000: /home/user/linux/build/net/packet/af_packet.ko + loading @0xffffffffa003c000: /home/user/linux/build/fs/fuse/fuse.ko + ... + loading @0xffffffffa0000000: /home/user/linux/build/drivers/ata/ata_generic.ko + + o Set a breakpoint on some not yet loaded module function, e.g.: + (gdb) b btrfs_init_sysfs + Function "btrfs_init_sysfs" not defined. + Make breakpoint pending on future shared library load? (y or [n]) y + Breakpoint 1 (btrfs_init_sysfs) pending. + + o Continue the target + (gdb) c + + o Load the module on the target and watch the symbols being loaded as well as + the breakpoint hit: + loading @0xffffffffa0034000: /home/user/linux/build/lib/libcrc32c.ko + loading @0xffffffffa0050000: /home/user/linux/build/lib/lzo/lzo_compress.ko + loading @0xffffffffa006e000: /home/user/linux/build/lib/zlib_deflate/zlib_deflate.ko + loading @0xffffffffa01b1000: /home/user/linux/build/fs/btrfs/btrfs.ko + + Breakpoint 1, btrfs_init_sysfs () at /home/user/linux/fs/btrfs/sysfs.c:36 + 36 btrfs_kset = kset_create_and_add("btrfs", NULL, fs_kobj); + + o Dump the log buffer of the target kernel: + (gdb) lx-dmesg + [ 0.000000] Initializing cgroup subsys cpuset + [ 0.000000] Initializing cgroup subsys cpu + [ 0.000000] Linux version 3.8.0-rc4-dbg+ (... + [ 0.000000] Command line: root=/dev/sda2 resume=/dev/sda1 vga=0x314 + [ 0.000000] e820: BIOS-provided physical RAM map: + [ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable + [ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved + .... + + o Examine fields of the current task struct: + (gdb) p $lx_current().pid + $1 = 4998 + (gdb) p $lx_current().comm + $2 = "modprobe\000\000\000\000\000\000\000" + + o Make use of the per-cpu function for the current or a specified CPU: + (gdb) p $lx_per_cpu("runqueues").nr_running + $3 = 1 + (gdb) p $lx_per_cpu("runqueues", 2).nr_running + $4 = 0 + + o Dig into hrtimers using the container_of helper: + (gdb) set $next = $lx_per_cpu("hrtimer_bases").clock_base[0].active.next + (gdb) p *$container_of($next, "struct hrtimer", "node") + $5 = { + node = { + node = { + __rb_parent_color = 18446612133355256072, + rb_right = 0x0 <irq_stack_union>, + rb_left = 0x0 <irq_stack_union> + }, + expires = { + tv64 = 1835268000000 + } + }, + _softexpires = { + tv64 = 1835268000000 + }, + function = 0xffffffff81078232 <tick_sched_timer>, + base = 0xffff88003fd0d6f0, + state = 1, + start_pid = 0, + start_site = 0xffffffff81055c1f <hrtimer_start_range_ns+20>, + start_comm = "swapper/2\000\000\000\000\000\000" + } + + +List of commands and functions +------------------------------ + +The number of commands and convenience functions may evolve over the time, +this is just a snapshot of the initial version: + + (gdb) apropos lx + function lx_current -- Return current task + function lx_module -- Find module by name and return the module variable + function lx_per_cpu -- Return per-cpu variable + function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable + function lx_thread_info -- Calculate Linux thread_info from task variable + lx-dmesg -- Print Linux kernel log buffer + lx-lsmod -- List currently loaded modules + lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules + +Detailed help can be obtained via "help <command-name>" for commands and "help +function <function-name>" for convenience functions. diff --git a/MAINTAINERS b/MAINTAINERS index 3eee4c7739ae..0597c5b3c4ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4232,6 +4232,11 @@ W: http://www.icp-vortex.com/ S: Supported F: drivers/scsi/gdt* +GDB KERNEL DEBUGGING HELPER SCRIPTS +M: Jan Kiszka <jan.kiszka@siemens.com> +S: Supported +F: scripts/gdb/ + GEMTEK FM RADIO RECEIVER DRIVER M: Hans Verkuil <hverkuil@xs4all.nl> L: linux-media@vger.kernel.org @@ -927,6 +927,9 @@ endif ifdef CONFIG_BUILD_DOCSRC $(Q)$(MAKE) $(build)=Documentation endif +ifdef CONFIG_GDB_SCRIPTS + $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py +endif +$(call if_changed,link-vmlinux) # The actual objects are generated when descending, @@ -1181,7 +1184,7 @@ MRPROPER_FILES += .config .config.old .version .old_version $(version_h) \ Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ signing_key.priv signing_key.x509 x509.genkey \ extra_certificates signing_key.x509.keyid \ - signing_key.x509.signer + signing_key.x509.signer vmlinux-gdb.py # clean - Delete most, but leave enough to build external modules # diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index f96d1ec24189..1a74446fd9e5 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -96,8 +96,6 @@ int default_machine_kexec_prepare(struct kimage *image) return 0; } -#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) - static void copy_segments(unsigned long ind) { unsigned long entry; diff --git a/fs/affs/affs.h b/fs/affs/affs.h index ff44ff3ff015..c8764bd7497d 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -30,6 +30,8 @@ #define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2) #define AFFS_AC_MASK (AFFS_AC_SIZE-1) +#define AFFSNAMEMAX 30U + struct affs_ext_key { u32 ext; /* idx of the extended block */ u32 key; /* block number */ diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index c852f2fa1710..388da1ea815d 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -30,7 +30,7 @@ affs_insert_hash(struct inode *dir, struct buffer_head *bh) ino = bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); - pr_debug("%s(dir=%u, ino=%d)\n", __func__, (u32)dir->i_ino, ino); + pr_debug("%s(dir=%lu, ino=%d)\n", __func__, dir->i_ino, ino); dir_bh = affs_bread(sb, dir->i_ino); if (!dir_bh) @@ -80,8 +80,8 @@ affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) sb = dir->i_sb; rem_ino = rem_bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); - pr_debug("%s(dir=%d, ino=%d, hashval=%d)\n", - __func__, (u32)dir->i_ino, rem_ino, offset); + pr_debug("%s(dir=%lu, ino=%d, hashval=%d)\n", __func__, dir->i_ino, + rem_ino, offset); bh = affs_bread(sb, dir->i_ino); if (!bh) @@ -483,11 +483,10 @@ affs_check_name(const unsigned char *name, int len, bool notruncate) { int i; - if (len > 30) { + if (len > AFFSNAMEMAX) { if (notruncate) return -ENAMETOOLONG; - else - len = 30; + len = AFFSNAMEMAX; } for (i = 0; i < len; i++) { if (name[i] < ' ' || name[i] == ':' @@ -508,7 +507,7 @@ affs_check_name(const unsigned char *name, int len, bool notruncate) int affs_copy_name(unsigned char *bstr, struct dentry *dentry) { - int len = min(dentry->d_name.len, 30u); + u32 len = min(dentry->d_name.len, AFFSNAMEMAX); *bstr++ = len; memcpy(bstr, dentry->d_name.name, len); diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c index c8de51185c23..675148950fed 100644 --- a/fs/affs/bitmap.c +++ b/fs/affs/bitmap.c @@ -99,7 +99,6 @@ err_bh_read: err_range: affs_error(sb, "affs_free_block","Block %u outside partition", block); - return; } /* diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 59f07bec92a6..ac4f318aafba 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -54,8 +54,7 @@ affs_readdir(struct file *file, struct dir_context *ctx) u32 ino; int error = 0; - pr_debug("%s(ino=%lu,f_pos=%lx)\n", - __func__, inode->i_ino, (unsigned long)ctx->pos); + pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos); if (ctx->pos < 2) { file->private_data = (void *)0; @@ -115,11 +114,11 @@ inside: break; } - namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); + namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], + (u8)AFFSNAMEMAX); name = AFFS_TAIL(sb, fh_bh)->name + 1; - pr_debug("readdir(): dir_emit(\"%.*s\", " - "ino=%u), hash=%d, f_pos=%x\n", - namelen, name, ino, hash_pos, (u32)ctx->pos); + pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n", + namelen, name, ino, hash_pos, ctx->pos); if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN)) goto done; diff --git a/fs/affs/file.c b/fs/affs/file.c index 8faa6593ca6d..d2468bf95669 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -180,8 +180,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext) ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension); if (ext < AFFS_I(inode)->i_extcnt) goto read_ext; - if (ext > AFFS_I(inode)->i_extcnt) - BUG(); + BUG_ON(ext > AFFS_I(inode)->i_extcnt); bh = affs_alloc_extblock(inode, bh, ext); if (IS_ERR(bh)) return bh; @@ -198,8 +197,7 @@ affs_get_extblock_slow(struct inode *inode, u32 ext) struct buffer_head *prev_bh; /* allocate a new extended block */ - if (ext > AFFS_I(inode)->i_extcnt) - BUG(); + BUG_ON(ext > AFFS_I(inode)->i_extcnt); /* get previous extended block */ prev_bh = affs_get_extblock(inode, ext - 1); @@ -299,8 +297,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul struct buffer_head *ext_bh; u32 ext; - pr_debug("%s(%u, %lu)\n", - __func__, (u32)inode->i_ino, (unsigned long)block); + pr_debug("%s(%lu, %llu)\n", __func__, inode->i_ino, + (unsigned long long)block); BUG_ON(block > (sector_t)0x7fffffffUL); @@ -330,8 +328,9 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul /* store new block */ if (bh_result->b_blocknr) - affs_warning(sb, "get_block", "block already set (%lx)", - (unsigned long)bh_result->b_blocknr); + affs_warning(sb, "get_block", + "block already set (%llx)", + (unsigned long long)bh_result->b_blocknr); AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr); AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1); affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1); @@ -353,8 +352,8 @@ affs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_resul return 0; err_big: - affs_error(inode->i_sb, "get_block", "strange block request %d", - (int)block); + affs_error(inode->i_sb, "get_block", "strange block request %llu", + (unsigned long long)block); return -EIO; err_ext: // unlock cache @@ -399,6 +398,13 @@ affs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, size_t count = iov_iter_count(iter); ssize_t ret; + if (rw == WRITE) { + loff_t size = offset + count; + + if (AFFS_I(inode)->mmu_private < size) + return 0; + } + ret = blockdev_direct_IO(rw, iocb, inode, iter, offset, affs_get_block); if (ret < 0 && (rw & WRITE)) affs_write_failed(mapping, offset + count); @@ -503,7 +509,7 @@ affs_do_readpage_ofs(struct page *page, unsigned to) u32 bidx, boff, bsize; u32 tmp; - pr_debug("%s(%u, %ld, 0, %d)\n", __func__, (u32)inode->i_ino, + pr_debug("%s(%lu, %ld, 0, %d)\n", __func__, inode->i_ino, page->index, to); BUG_ON(to > PAGE_CACHE_SIZE); kmap(page); @@ -539,7 +545,7 @@ affs_extent_file_ofs(struct inode *inode, u32 newsize) u32 size, bsize; u32 tmp; - pr_debug("%s(%u, %d)\n", __func__, (u32)inode->i_ino, newsize); + pr_debug("%s(%lu, %d)\n", __func__, inode->i_ino, newsize); bsize = AFFS_SB(sb)->s_data_blksize; bh = NULL; size = AFFS_I(inode)->mmu_private; @@ -608,7 +614,7 @@ affs_readpage_ofs(struct file *file, struct page *page) u32 to; int err; - pr_debug("%s(%u, %ld)\n", __func__, (u32)inode->i_ino, page->index); + pr_debug("%s(%lu, %ld)\n", __func__, inode->i_ino, page->index); to = PAGE_CACHE_SIZE; if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) { to = inode->i_size & ~PAGE_CACHE_MASK; @@ -631,8 +637,8 @@ static int affs_write_begin_ofs(struct file *file, struct address_space *mapping pgoff_t index; int err = 0; - pr_debug("%s(%u, %llu, %llu)\n", __func__, (u32)inode->i_ino, - (unsigned long long)pos, (unsigned long long)pos + len); + pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, + pos + len); if (pos > AFFS_I(inode)->mmu_private) { /* XXX: this probably leaves a too-big i_size in case of * failure. Should really be updating i_size at write_end time @@ -681,9 +687,8 @@ static int affs_write_end_ofs(struct file *file, struct address_space *mapping, * due to write_begin. */ - pr_debug("%s(%u, %llu, %llu)\n", - __func__, (u32)inode->i_ino, (unsigned long long)pos, - (unsigned long long)pos + len); + pr_debug("%s(%lu, %llu, %llu)\n", __func__, inode->i_ino, pos, + pos + len); bsize = AFFS_SB(sb)->s_data_blksize; data = page_address(page); @@ -831,8 +836,8 @@ affs_truncate(struct inode *inode) struct buffer_head *ext_bh; int i; - pr_debug("truncate(inode=%d, oldsize=%u, newsize=%u)\n", - (u32)inode->i_ino, (u32)AFFS_I(inode)->mmu_private, (u32)inode->i_size); + pr_debug("truncate(inode=%lu, oldsize=%llu, newsize=%llu)\n", + inode->i_ino, AFFS_I(inode)->mmu_private, inode->i_size); last_blk = 0; ext = 0; @@ -863,7 +868,7 @@ affs_truncate(struct inode *inode) if (IS_ERR(ext_bh)) { affs_warning(sb, "truncate", "unexpected read error for ext block %u (%ld)", - (unsigned int)ext, PTR_ERR(ext_bh)); + ext, PTR_ERR(ext_bh)); return; } if (AFFS_I(inode)->i_lc) { @@ -911,7 +916,7 @@ affs_truncate(struct inode *inode) if (IS_ERR(bh)) { affs_warning(sb, "truncate", "unexpected read error for last block %u (%ld)", - (unsigned int)ext, PTR_ERR(bh)); + ext, PTR_ERR(bh)); return; } tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next); diff --git a/fs/affs/inode.c b/fs/affs/inode.c index d0609a282e1d..6f34510449e8 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -13,8 +13,6 @@ #include <linux/gfp.h> #include "affs.h" -extern const struct inode_operations affs_symlink_inode_operations; - struct inode *affs_iget(struct super_block *sb, unsigned long ino) { struct affs_sb_info *sbi = AFFS_SB(sb); @@ -348,9 +346,8 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3 u32 block = 0; int retval; - pr_debug("%s(dir=%u, inode=%u, \"%pd\", type=%d)\n", - __func__, (u32)dir->i_ino, - (u32)inode->i_ino, dentry, type); + pr_debug("%s(dir=%lu, inode=%lu, \"%pd\", type=%d)\n", __func__, + dir->i_ino, inode->i_ino, dentry, type); retval = -EIO; bh = affs_bread(sb, inode->i_ino); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index bbc38530e924..ffb7bd82c2a5 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -64,15 +64,16 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper, bool notruncate) { const u8 *name = qstr->name; unsigned long hash; - int i; + int retval; + u32 len; - i = affs_check_name(qstr->name, qstr->len, notruncate); - if (i) - return i; + retval = affs_check_name(qstr->name, qstr->len, notruncate); + if (retval) + return retval; hash = init_name_hash(); - i = min(qstr->len, 30u); - for (; i > 0; name++, i--) + len = min(qstr->len, AFFSNAMEMAX); + for (; len > 0; name++, len--) hash = partial_name_hash(toupper(*name), hash); qstr->hash = end_name_hash(hash); @@ -114,10 +115,10 @@ static inline int __affs_compare_dentry(unsigned int len, * If the names are longer than the allowed 30 chars, * the excess is ignored, so their length may differ. */ - if (len >= 30) { - if (name->len < 30) + if (len >= AFFSNAMEMAX) { + if (name->len < AFFSNAMEMAX) return 1; - len = 30; + len = AFFSNAMEMAX; } else if (len != name->len) return 1; @@ -156,10 +157,10 @@ affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) const u8 *name = dentry->d_name.name; int len = dentry->d_name.len; - if (len >= 30) { - if (*name2 < 30) + if (len >= AFFSNAMEMAX) { + if (*name2 < AFFSNAMEMAX) return 0; - len = 30; + len = AFFSNAMEMAX; } else if (len != *name2) return 0; @@ -173,9 +174,9 @@ int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) { toupper_t toupper = affs_get_toupper(sb); - int hash; + u32 hash; - hash = len = min(len, 30u); + hash = len = min(len, AFFSNAMEMAX); for (; len > 0; len--) hash = (hash * 13 + toupper(*name++)) & 0x7ff; @@ -248,9 +249,8 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) int affs_unlink(struct inode *dir, struct dentry *dentry) { - pr_debug("%s(dir=%d, %lu \"%pd\")\n", - __func__, (u32)dir->i_ino, dentry->d_inode->i_ino, - dentry); + pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, + dentry->d_inode->i_ino, dentry); return affs_remove_header(dentry); } @@ -317,9 +317,8 @@ affs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) int affs_rmdir(struct inode *dir, struct dentry *dentry) { - pr_debug("%s(dir=%u, %lu \"%pd\")\n", - __func__, (u32)dir->i_ino, dentry->d_inode->i_ino, - dentry); + pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, + dentry->d_inode->i_ino, dentry); return affs_remove_header(dentry); } @@ -404,8 +403,7 @@ affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; - pr_debug("%s(%u, %u, \"%pd\")\n", - __func__, (u32)inode->i_ino, (u32)dir->i_ino, + pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino, dentry); return affs_add_entry(dir, inode, dentry, ST_LINKFILE); @@ -419,9 +417,8 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct buffer_head *bh = NULL; int retval; - pr_debug("%s(old=%u,\"%pd\" to new=%u,\"%pd\")\n", - __func__, (u32)old_dir->i_ino, old_dentry, - (u32)new_dir->i_ino, new_dentry); + pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__, + old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry); retval = affs_check_name(new_dentry->d_name.name, new_dentry->d_name.len, diff --git a/fs/affs/super.c b/fs/affs/super.c index f754ab68a840..4cf0e9113fb6 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -432,39 +432,39 @@ got_root: sb->s_flags |= MS_RDONLY; } switch (chksum) { - case MUFS_FS: - case MUFS_INTLFFS: - case MUFS_DCFFS: - sbi->s_flags |= SF_MUFS; - /* fall thru */ - case FS_INTLFFS: - case FS_DCFFS: - sbi->s_flags |= SF_INTL; - break; - case MUFS_FFS: - sbi->s_flags |= SF_MUFS; - break; - case FS_FFS: - break; - case MUFS_OFS: - sbi->s_flags |= SF_MUFS; - /* fall thru */ - case FS_OFS: - sbi->s_flags |= SF_OFS; - sb->s_flags |= MS_NOEXEC; - break; - case MUFS_DCOFS: - case MUFS_INTLOFS: - sbi->s_flags |= SF_MUFS; - case FS_DCOFS: - case FS_INTLOFS: - sbi->s_flags |= SF_INTL | SF_OFS; - sb->s_flags |= MS_NOEXEC; - break; - default: - pr_err("Unknown filesystem on device %s: %08X\n", - sb->s_id, chksum); - return -EINVAL; + case MUFS_FS: + case MUFS_INTLFFS: + case MUFS_DCFFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_INTLFFS: + case FS_DCFFS: + sbi->s_flags |= SF_INTL; + break; + case MUFS_FFS: + sbi->s_flags |= SF_MUFS; + break; + case FS_FFS: + break; + case MUFS_OFS: + sbi->s_flags |= SF_MUFS; + /* fall thru */ + case FS_OFS: + sbi->s_flags |= SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + case MUFS_DCOFS: + case MUFS_INTLOFS: + sbi->s_flags |= SF_MUFS; + case FS_DCOFS: + case FS_INTLOFS: + sbi->s_flags |= SF_INTL | SF_OFS; + sb->s_flags |= MS_NOEXEC; + break; + default: + pr_err("Unknown filesystem on device %s: %08X\n", + sb->s_id, chksum); + return -EINVAL; } if (mount_flags & SF_VERBOSE) { @@ -584,7 +584,7 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = free; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); - buf->f_namelen = 30; + buf->f_namelen = AFFSNAMEMAX; return 0; } @@ -602,6 +602,7 @@ static void affs_kill_sb(struct super_block *sb) affs_free_bitmap(sb); affs_brelse(sbi->s_root_bh); kfree(sbi->s_prefix); + mutex_destroy(&sbi->s_bmlock); kfree(sbi); } } diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index edf47774b03d..e089f1985fca 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -274,9 +274,9 @@ more: static struct inode * befs_alloc_inode(struct super_block *sb) { - struct befs_inode_info *bi; - bi = (struct befs_inode_info *)kmem_cache_alloc(befs_inode_cachep, - GFP_KERNEL); + struct befs_inode_info *bi; + + bi = kmem_cache_alloc(befs_inode_cachep, GFP_KERNEL); if (!bi) return NULL; return &bi->vfs_inode; diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 86c893884eb9..281ee011bb6a 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -28,29 +28,6 @@ #include "coda_int.h" -/* dir inode-ops */ -static int coda_create(struct inode *dir, struct dentry *new, umode_t mode, bool excl); -static struct dentry *coda_lookup(struct inode *dir, struct dentry *target, unsigned int flags); -static int coda_link(struct dentry *old_dentry, struct inode *dir_inode, - struct dentry *entry); -static int coda_unlink(struct inode *dir_inode, struct dentry *entry); -static int coda_symlink(struct inode *dir_inode, struct dentry *entry, - const char *symname); -static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, umode_t mode); -static int coda_rmdir(struct inode *dir_inode, struct dentry *entry); -static int coda_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry); - -/* dir file-ops */ -static int coda_readdir(struct file *file, struct dir_context *ctx); - -/* dentry ops */ -static int coda_dentry_revalidate(struct dentry *de, unsigned int flags); -static int coda_dentry_delete(const struct dentry *); - -/* support routines */ -static int coda_venus_readdir(struct file *, struct dir_context *); - /* same as fs/bad_inode.c */ static int coda_return_EIO(void) { @@ -58,38 +35,6 @@ static int coda_return_EIO(void) } #define CODA_EIO_ERROR ((void *) (coda_return_EIO)) -const struct dentry_operations coda_dentry_operations = -{ - .d_revalidate = coda_dentry_revalidate, - .d_delete = coda_dentry_delete, -}; - -const struct inode_operations coda_dir_inode_operations = -{ - .create = coda_create, - .lookup = coda_lookup, - .link = coda_link, - .unlink = coda_unlink, - .symlink = coda_symlink, - .mkdir = coda_mkdir, - .rmdir = coda_rmdir, - .mknod = CODA_EIO_ERROR, - .rename = coda_rename, - .permission = coda_permission, - .getattr = coda_getattr, - .setattr = coda_setattr, -}; - -const struct file_operations coda_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate = coda_readdir, - .open = coda_open, - .release = coda_release, - .fsync = coda_fsync, -}; - - /* inode operations for directories */ /* access routines: lookup, readlink, permission */ static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) @@ -374,33 +319,6 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry, return error; } - -/* file operations for directories */ -static int coda_readdir(struct file *coda_file, struct dir_context *ctx) -{ - struct coda_file_info *cfi; - struct file *host_file; - int ret; - - cfi = CODA_FTOC(coda_file); - BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - host_file = cfi->cfi_container; - - if (host_file->f_op->iterate) { - struct inode *host_inode = file_inode(host_file); - mutex_lock(&host_inode->i_mutex); - ret = -ENOENT; - if (!IS_DEADDIR(host_inode)) { - ret = host_file->f_op->iterate(host_file, ctx); - file_accessed(host_file); - } - mutex_unlock(&host_inode->i_mutex); - return ret; - } - /* Venus: we must read Venus dirents from a file */ - return coda_venus_readdir(coda_file, ctx); -} - static inline unsigned int CDT2DT(unsigned char cdt) { unsigned int dt; @@ -495,6 +413,33 @@ out: return 0; } +/* file operations for directories */ +static int coda_readdir(struct file *coda_file, struct dir_context *ctx) +{ + struct coda_file_info *cfi; + struct file *host_file; + int ret; + + cfi = CODA_FTOC(coda_file); + BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); + host_file = cfi->cfi_container; + + if (host_file->f_op->iterate) { + struct inode *host_inode = file_inode(host_file); + + mutex_lock(&host_inode->i_mutex); + ret = -ENOENT; + if (!IS_DEADDIR(host_inode)) { + ret = host_file->f_op->iterate(host_file, ctx); + file_accessed(host_file); + } + mutex_unlock(&host_inode->i_mutex); + return ret; + } + /* Venus: we must read Venus dirents from a file */ + return coda_venus_readdir(coda_file, ctx); +} + /* called when a cache lookup succeeds */ static int coda_dentry_revalidate(struct dentry *de, unsigned int flags) { @@ -603,3 +548,32 @@ int coda_revalidate_inode(struct inode *inode) } return 0; } + +const struct dentry_operations coda_dentry_operations = { + .d_revalidate = coda_dentry_revalidate, + .d_delete = coda_dentry_delete, +}; + +const struct inode_operations coda_dir_inode_operations = { + .create = coda_create, + .lookup = coda_lookup, + .link = coda_link, + .unlink = coda_unlink, + .symlink = coda_symlink, + .mkdir = coda_mkdir, + .rmdir = coda_rmdir, + .mknod = CODA_EIO_ERROR, + .rename = coda_rename, + .permission = coda_permission, + .getattr = coda_getattr, + .setattr = coda_setattr, +}; + +const struct file_operations coda_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = coda_readdir, + .open = coda_open, + .release = coda_release, + .fsync = coda_fsync, +}; diff --git a/fs/eventfd.c b/fs/eventfd.c index 4b0a226024fa..8d0c0df01854 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -118,18 +118,18 @@ static unsigned int eventfd_poll(struct file *file, poll_table *wait) { struct eventfd_ctx *ctx = file->private_data; unsigned int events = 0; - unsigned long flags; + u64 count; poll_wait(file, &ctx->wqh, wait); + smp_rmb(); + count = ctx->count; - spin_lock_irqsave(&ctx->wqh.lock, flags); - if (ctx->count > 0) + if (count > 0) events |= POLLIN; - if (ctx->count == ULLONG_MAX) + if (count == ULLONG_MAX) events |= POLLERR; - if (ULLONG_MAX - 1 > ctx->count) + if (ULLONG_MAX - 1 > count) events |= POLLOUT; - spin_unlock_irqrestore(&ctx->wqh.lock, flags); return events; } diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 7b41a2dcdd76..497c7c5263c7 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -580,7 +580,7 @@ static void fat_set_state(struct super_block *sb, { struct buffer_head *bh; struct fat_boot_sector *b; - struct msdos_sb_info *sbi = sb->s_fs_info; + struct msdos_sb_info *sbi = MSDOS_SB(sb); /* do not change any thing if mounted read only */ if ((sb->s_flags & MS_RDONLY) && !force) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index a90d6d354199..4e61388ec03d 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -546,8 +546,8 @@ static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr) nhdr_ptr = notes_section; while (nhdr_ptr->n_namesz != 0) { sz = sizeof(Elf64_Nhdr) + - ((nhdr_ptr->n_namesz + 3) & ~3) + - ((nhdr_ptr->n_descsz + 3) & ~3); + (((u64)nhdr_ptr->n_namesz + 3) & ~3) + + (((u64)nhdr_ptr->n_descsz + 3) & ~3); if ((real_sz + sz) > max_sz) { pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n", nhdr_ptr->n_namesz, nhdr_ptr->n_descsz); @@ -732,8 +732,8 @@ static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr) nhdr_ptr = notes_section; while (nhdr_ptr->n_namesz != 0) { sz = sizeof(Elf32_Nhdr) + - ((nhdr_ptr->n_namesz + 3) & ~3) + - ((nhdr_ptr->n_descsz + 3) & ~3); + (((u64)nhdr_ptr->n_namesz + 3) & ~3) + + (((u64)nhdr_ptr->n_descsz + 3) & ~3); if ((real_sz + sz) > max_sz) { pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n", nhdr_ptr->n_namesz, nhdr_ptr->n_descsz); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index a7eec9888f10..e72401e1f995 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2766,7 +2766,7 @@ static int reiserfs_write_begin(struct file *file, int old_ref = 0; inode = mapping->host; - *fsdata = 0; + *fsdata = NULL; if (flags & AOP_FLAG_CONT_EXPAND && (pos & (inode->i_sb->s_blocksize - 1)) == 0) { pos ++; diff --git a/fs/ufs/super.c b/fs/ufs/super.c index da73801301d5..8092d3759a5e 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -95,22 +95,18 @@ void lock_ufs(struct super_block *sb) { -#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT) struct ufs_sb_info *sbi = UFS_SB(sb); mutex_lock(&sbi->mutex); sbi->mutex_owner = current; -#endif } void unlock_ufs(struct super_block *sb) { -#if defined(CONFIG_SMP) || defined (CONFIG_PREEMPT) struct ufs_sb_info *sbi = UFS_SB(sb); sbi->mutex_owner = NULL; mutex_unlock(&sbi->mutex); -#endif } static struct inode *ufs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation) @@ -1415,9 +1411,11 @@ static struct kmem_cache * ufs_inode_cachep; static struct inode *ufs_alloc_inode(struct super_block *sb) { struct ufs_inode_info *ei; - ei = (struct ufs_inode_info *)kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS); + + ei = kmem_cache_alloc(ufs_inode_cachep, GFP_NOFS); if (!ei) return NULL; + ei->vfs_inode.i_version = 1; return &ei->vfs_inode; } diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 9d957b7ae095..e60a745ac198 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -1,6 +1,19 @@ #ifndef LINUX_KEXEC_H #define LINUX_KEXEC_H +#define IND_DESTINATION_BIT 0 +#define IND_INDIRECTION_BIT 1 +#define IND_DONE_BIT 2 +#define IND_SOURCE_BIT 3 + +#define IND_DESTINATION (1 << IND_DESTINATION_BIT) +#define IND_INDIRECTION (1 << IND_INDIRECTION_BIT) +#define IND_DONE (1 << IND_DONE_BIT) +#define IND_SOURCE (1 << IND_SOURCE_BIT) +#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE) + +#if !defined(__ASSEMBLY__) + #include <uapi/linux/kexec.h> #ifdef CONFIG_KEXEC @@ -64,10 +77,6 @@ */ typedef unsigned long kimage_entry_t; -#define IND_DESTINATION 0x1 -#define IND_INDIRECTION 0x2 -#define IND_DONE 0x4 -#define IND_SOURCE 0x8 struct kexec_segment { /* @@ -122,8 +131,6 @@ struct kimage { kimage_entry_t *entry; kimage_entry_t *last_entry; - unsigned long destination; - unsigned long start; struct page *control_code_page; struct page *swap_page; @@ -313,4 +320,7 @@ struct task_struct; static inline void crash_kexec(struct pt_regs *regs) { } static inline int kexec_should_crash(struct task_struct *p) { return 0; } #endif /* CONFIG_KEXEC */ + +#endif /* !defined(__ASSEBMLY__) */ + #endif /* LINUX_KEXEC_H */ diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h index 57e75ae9910f..fb31765e935a 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h @@ -51,7 +51,7 @@ struct rb_root { #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) -/* 'empty' nodes are nodes that are known not to be inserted in an rbree */ +/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */ #define RB_EMPTY_NODE(node) \ ((node)->__rb_parent_color == (unsigned long)(node)) #define RB_CLEAR_NODE(node) \ diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h index 6925f5b42f89..99048e501b88 100644 --- a/include/uapi/linux/kexec.h +++ b/include/uapi/linux/kexec.h @@ -55,12 +55,6 @@ struct kexec_segment { size_t memsz; }; -/* Load a new kernel image as described by the kexec_segment array - * consisting of passed number of segments at the entry-point address. - * The flags allow different useage types. - */ -extern int kexec_load(void *, size_t, struct kexec_segment *, - unsigned long int); #endif /* __KERNEL__ */ #endif /* _UAPILINUX_KEXEC_H */ diff --git a/ipc/sem.c b/ipc/sem.c index 6115146563f9..92842113c6a9 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1941,7 +1941,7 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, queue.sleeper = current; sleep_again: - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); sem_unlock(sma, locknum); rcu_read_unlock(); diff --git a/kernel/kexec.c b/kernel/kexec.c index c85277639b34..38c25b1f2fd5 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -444,7 +444,7 @@ arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, } /* - * Free up memory used by kernel, initrd, and comand line. This is temporary + * Free up memory used by kernel, initrd, and command line. This is temporary * memory allocation which is not needed any more after these buffers have * been loaded into separate segments and have been copied elsewhere. */ @@ -856,8 +856,6 @@ static int kimage_set_destination(struct kimage *image, destination &= PAGE_MASK; result = kimage_add_entry(image, destination | IND_DESTINATION); - if (result == 0) - image->destination = destination; return result; } @@ -869,8 +867,6 @@ static int kimage_add_page(struct kimage *image, unsigned long page) page &= PAGE_MASK; result = kimage_add_entry(image, page | IND_SOURCE); - if (result == 0) - image->destination += PAGE_SIZE; return result; } @@ -1288,19 +1284,22 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, if (nr_segments > 0) { unsigned long i; - /* Loading another kernel to reboot into */ - if ((flags & KEXEC_ON_CRASH) == 0) - result = kimage_alloc_init(&image, entry, nr_segments, - segments, flags); - /* Loading another kernel to switch to if this one crashes */ - else if (flags & KEXEC_ON_CRASH) { - /* Free any current crash dump kernel before + if (flags & KEXEC_ON_CRASH) { + /* + * Loading another kernel to switch to if this one + * crashes. Free any current crash dump kernel before * we corrupt it. */ + kimage_free(xchg(&kexec_crash_image, NULL)); result = kimage_alloc_init(&image, entry, nr_segments, segments, flags); crash_map_reserved_pages(); + } else { + /* Loading another kernel to reboot into. */ + + result = kimage_alloc_init(&image, entry, nr_segments, + segments, flags); } if (result) goto out; diff --git a/kernel/module.c b/kernel/module.c index 8426ad48362c..b34813f725e9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3025,8 +3025,13 @@ static void do_free_init(struct rcu_head *head) kfree(m); } -/* This is where the real work happens */ -static int do_init_module(struct module *mod) +/* + * This is where the real work happens. + * + * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb + * helper command 'lx-symbols'. + */ +static noinline int do_init_module(struct module *mod) { int ret = 0; struct mod_initfree *freeinit; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 1eb9d90c3af9..227fec36b12a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1077,7 +1077,6 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr, } #if defined CONFIG_COMPAT -#include <linux/compat.h> int compat_ptrace_request(struct task_struct *child, compat_long_t request, compat_ulong_t addr, compat_ulong_t data) diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 4ef9687ac115..4f44028943e6 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -629,7 +629,9 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd) switch (action) { case SECCOMP_RET_ERRNO: - /* Set the low-order 16-bits as a errno. */ + /* Set low-order bits as an errno, capped at MAX_ERRNO. */ + if (data > MAX_ERRNO) + data = MAX_ERRNO; syscall_set_return_value(current, task_pt_regs(current), -data, 0); goto skip; diff --git a/kernel/signal.c b/kernel/signal.c index 33a52759cc0e..a390499943e4 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3550,7 +3550,7 @@ SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler) SYSCALL_DEFINE0(pause) { while (!signal_pending(current)) { - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); schedule(); } return -ERESTARTNOHAND; @@ -3563,7 +3563,7 @@ int sigsuspend(sigset_t *set) current->saved_sigmask = current->blocked; set_current_blocked(set); - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); schedule(); set_restore_sigmask(); return -ERESTARTNOHAND; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ecb3516f6546..c5cefb3c009c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -167,6 +167,17 @@ config DEBUG_INFO_DWARF4 But it significantly improves the success of resolving variables in gdb on optimized code. +config GDB_SCRIPTS + bool "Provide GDB scripts for kernel debugging" + depends on DEBUG_INFO + help + This creates the required links to GDB helper scripts in the + build directory. If you load vmlinux into gdb, the helper + scripts will be automatically imported by gdb as well, and + additional functions are available to analyze a Linux kernel + instance. See Documentation/gdb-kernel-debugging.txt for further + details. + config ENABLE_WARN_DEPRECATED bool "Enable __deprecated logic" default y diff --git a/samples/seccomp/bpf-fancy.c b/samples/seccomp/bpf-fancy.c index 8eb483aaec46..e8b24f443709 100644 --- a/samples/seccomp/bpf-fancy.c +++ b/samples/seccomp/bpf-fancy.c @@ -25,7 +25,9 @@ int main(int argc, char **argv) { - struct bpf_labels l; + struct bpf_labels l = { + .count = 0, + }; static const char msg1[] = "Please type something: "; static const char msg2[] = "You typed: "; char buf[256]; diff --git a/samples/seccomp/bpf-helper.c b/samples/seccomp/bpf-helper.c index 579cfe331886..05cb4d5ff9f5 100644 --- a/samples/seccomp/bpf-helper.c +++ b/samples/seccomp/bpf-helper.c @@ -10,6 +10,7 @@ */ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "bpf-helper.h" @@ -63,6 +64,11 @@ __u32 seccomp_bpf_label(struct bpf_labels *labels, const char *label) { struct __bpf_label *begin = labels->labels, *end; int id; + + if (labels->count == BPF_LABELS_MAX) { + fprintf(stderr, "Too many labels\n"); + exit(1); + } if (labels->count == 0) { begin->label = label; begin->location = 0xffffffff; diff --git a/scripts/Makefile b/scripts/Makefile index 72902b5f2721..2016a64497ab 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -36,6 +36,7 @@ subdir-$(CONFIG_MODVERSIONS) += genksyms subdir-y += mod subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_DTC) += dtc +subdir-$(CONFIG_GDB_SCRIPTS) += gdb # Let clean descend into subdirs subdir- += basic kconfig package diff --git a/scripts/gdb/Makefile b/scripts/gdb/Makefile new file mode 100644 index 000000000000..62f5f65becfd --- /dev/null +++ b/scripts/gdb/Makefile @@ -0,0 +1 @@ +subdir-y := linux diff --git a/scripts/gdb/linux/.gitignore b/scripts/gdb/linux/.gitignore new file mode 100644 index 000000000000..52e4e61140d1 --- /dev/null +++ b/scripts/gdb/linux/.gitignore @@ -0,0 +1,2 @@ +*.pyc +*.pyo diff --git a/scripts/gdb/linux/Makefile b/scripts/gdb/linux/Makefile new file mode 100644 index 000000000000..6cf1ecf61057 --- /dev/null +++ b/scripts/gdb/linux/Makefile @@ -0,0 +1,11 @@ +always := gdb-scripts + +SRCTREE := $(shell cd $(srctree) && /bin/pwd) + +$(obj)/gdb-scripts: +ifneq ($(KBUILD_SRC),) + $(Q)ln -fsn $(SRCTREE)/$(obj)/*.py $(objtree)/$(obj) +endif + @: + +clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py new file mode 100644 index 000000000000..4297b83fedef --- /dev/null +++ b/scripts/gdb/linux/cpus.py @@ -0,0 +1,135 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# per-cpu tools +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import tasks, utils + + +MAX_CPUS = 4096 + + +def get_current_cpu(): + if utils.get_gdbserver_type() == utils.GDBSERVER_QEMU: + return gdb.selected_thread().num - 1 + elif utils.get_gdbserver_type() == utils.GDBSERVER_KGDB: + tid = gdb.selected_thread().ptid[2] + if tid > (0x100000000 - MAX_CPUS - 2): + return 0x100000000 - tid - 2 + else: + return tasks.get_thread_info(tasks.get_task_by_pid(tid))['cpu'] + else: + raise gdb.GdbError("Sorry, obtaining the current CPU is not yet " + "supported with this gdb server.") + + +def per_cpu(var_ptr, cpu): + if cpu == -1: + cpu = get_current_cpu() + if utils.is_target_arch("sparc:v9"): + offset = gdb.parse_and_eval( + "trap_block[{0}].__per_cpu_base".format(str(cpu))) + else: + try: + offset = gdb.parse_and_eval( + "__per_cpu_offset[{0}]".format(str(cpu))) + except gdb.error: + # !CONFIG_SMP case + offset = 0 + pointer = var_ptr.cast(utils.get_long_type()) + offset + return pointer.cast(var_ptr.type).dereference() + + +cpu_mask = {} + + +def cpu_mask_invalidate(event): + global cpu_mask + cpu_mask = {} + gdb.events.stop.disconnect(cpu_mask_invalidate) + if hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.disconnect(cpu_mask_invalidate) + + +def cpu_list(mask_name): + global cpu_mask + mask = None + if mask_name in cpu_mask: + mask = cpu_mask[mask_name] + if mask is None: + mask = gdb.parse_and_eval(mask_name + ".bits") + if hasattr(gdb, 'events'): + cpu_mask[mask_name] = mask + gdb.events.stop.connect(cpu_mask_invalidate) + if hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.connect(cpu_mask_invalidate) + bits_per_entry = mask[0].type.sizeof * 8 + num_entries = mask.type.sizeof * 8 / bits_per_entry + entry = -1 + bits = 0 + + while True: + while bits == 0: + entry += 1 + if entry == num_entries: + return + bits = mask[entry] + if bits != 0: + bit = 0 + break + + while bits & 1 == 0: + bits >>= 1 + bit += 1 + + cpu = entry * bits_per_entry + bit + + bits >>= 1 + bit += 1 + + yield cpu + + +class PerCpu(gdb.Function): + """Return per-cpu variable. + +$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the +given CPU number. If CPU is omitted, the CPU of the current context is used. +Note that VAR has to be quoted as string.""" + + def __init__(self): + super(PerCpu, self).__init__("lx_per_cpu") + + def invoke(self, var_name, cpu=-1): + var_ptr = gdb.parse_and_eval("&" + var_name.string()) + return per_cpu(var_ptr, cpu) + + +PerCpu() + + +class LxCurrentFunc(gdb.Function): + """Return current task. + +$lx_current([CPU]): Return the per-cpu task variable for the given CPU +number. If CPU is omitted, the CPU of the current context is used.""" + + def __init__(self): + super(LxCurrentFunc, self).__init__("lx_current") + + def invoke(self, cpu=-1): + var_ptr = gdb.parse_and_eval("¤t_task") + return per_cpu(var_ptr, cpu).dereference() + + +LxCurrentFunc() diff --git a/scripts/gdb/linux/dmesg.py b/scripts/gdb/linux/dmesg.py new file mode 100644 index 000000000000..3c947f0c5dad --- /dev/null +++ b/scripts/gdb/linux/dmesg.py @@ -0,0 +1,65 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# kernel log buffer dump +# +# Copyright (c) Siemens AG, 2011, 2012 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +import string + +from linux import utils + + +class LxDmesg(gdb.Command): + """Print Linux kernel log buffer.""" + + def __init__(self): + super(LxDmesg, self).__init__("lx-dmesg", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + log_buf_addr = int(str(gdb.parse_and_eval("log_buf")).split()[0], 16) + log_first_idx = int(gdb.parse_and_eval("log_first_idx")) + log_next_idx = int(gdb.parse_and_eval("log_next_idx")) + log_buf_len = int(gdb.parse_and_eval("log_buf_len")) + + inf = gdb.inferiors()[0] + start = log_buf_addr + log_first_idx + if log_first_idx < log_next_idx: + log_buf_2nd_half = -1 + length = log_next_idx - log_first_idx + log_buf = inf.read_memory(start, length) + else: + log_buf_2nd_half = log_buf_len - log_first_idx + log_buf = inf.read_memory(start, log_buf_2nd_half) + \ + inf.read_memory(log_buf_addr, log_next_idx) + + pos = 0 + while pos < log_buf.__len__(): + length = utils.read_u16(log_buf[pos + 8:pos + 10]) + if length == 0: + if log_buf_2nd_half == -1: + gdb.write("Corrupted log buffer!\n") + break + pos = log_buf_2nd_half + continue + + text_len = utils.read_u16(log_buf[pos + 10:pos + 12]) + text = log_buf[pos + 16:pos + 16 + text_len] + time_stamp = utils.read_u64(log_buf[pos:pos + 8]) + + for line in memoryview(text).tobytes().splitlines(): + gdb.write("[{time:12.6f}] {line}\n".format( + time=time_stamp / 1000000000.0, + line=line)) + + pos += length + + +LxDmesg() diff --git a/scripts/gdb/linux/modules.py b/scripts/gdb/linux/modules.py new file mode 100644 index 000000000000..a1504c4f1900 --- /dev/null +++ b/scripts/gdb/linux/modules.py @@ -0,0 +1,103 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# module tools +# +# Copyright (c) Siemens AG, 2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import cpus, utils + + +module_type = utils.CachedType("struct module") + + +def module_list(): + global module_type + module_ptr_type = module_type.get_type().pointer() + modules = gdb.parse_and_eval("modules") + entry = modules['next'] + end_of_list = modules.address + + while entry != end_of_list: + yield utils.container_of(entry, module_ptr_type, "list") + entry = entry['next'] + + +def find_module_by_name(name): + for module in module_list(): + if module['name'].string() == name: + return module + return None + + +class LxModule(gdb.Function): + """Find module by name and return the module variable. + +$lx_module("MODULE"): Given the name MODULE, iterate over all loaded modules +of the target and return that module variable which MODULE matches.""" + + def __init__(self): + super(LxModule, self).__init__("lx_module") + + def invoke(self, mod_name): + mod_name = mod_name.string() + module = find_module_by_name(mod_name) + if module: + return module.dereference() + else: + raise gdb.GdbError("Unable to find MODULE " + mod_name) + + +LxModule() + + +class LxLsmod(gdb.Command): + """List currently loaded modules.""" + + _module_use_type = utils.CachedType("struct module_use") + + def __init__(self): + super(LxLsmod, self).__init__("lx-lsmod", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + gdb.write( + "Address{0} Module Size Used by\n".format( + " " if utils.get_long_type().sizeof == 8 else "")) + + for module in module_list(): + ref = 0 + module_refptr = module['refptr'] + for cpu in cpus.cpu_list("cpu_possible_mask"): + refptr = cpus.per_cpu(module_refptr, cpu) + ref += refptr['incs'] + ref -= refptr['decs'] + + gdb.write("{address} {name:<19} {size:>8} {ref}".format( + address=str(module['module_core']).split()[0], + name=module['name'].string(), + size=str(module['core_size']), + ref=str(ref))) + + source_list = module['source_list'] + t = self._module_use_type.get_type().pointer() + entry = source_list['next'] + first = True + while entry != source_list.address: + use = utils.container_of(entry, t, "source_list") + gdb.write("{separator}{name}".format( + separator=" " if first else ",", + name=use['source']['name'].string())) + first = False + entry = entry['next'] + gdb.write("\n") + + +LxLsmod() diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py new file mode 100644 index 000000000000..cd5bea965d4e --- /dev/null +++ b/scripts/gdb/linux/symbols.py @@ -0,0 +1,177 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# load kernel and module symbols +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb +import os +import re +import string + +from linux import modules, utils + + +if hasattr(gdb, 'Breakpoint'): + class LoadModuleBreakpoint(gdb.Breakpoint): + def __init__(self, spec, gdb_command): + super(LoadModuleBreakpoint, self).__init__(spec, internal=True) + self.silent = True + self.gdb_command = gdb_command + + def stop(self): + module = gdb.parse_and_eval("mod") + module_name = module['name'].string() + cmd = self.gdb_command + + # enforce update if object file is not found + cmd.module_files_updated = False + + # Disable pagination while reporting symbol (re-)loading. + # The console input is blocked in this context so that we would + # get stuck waiting for the user to acknowledge paged output. + show_pagination = gdb.execute("show pagination", to_string=True) + pagination = show_pagination.endswith("on.\n") + gdb.execute("set pagination off") + + if module_name in cmd.loaded_modules: + gdb.write("refreshing all symbols to reload module " + "'{0}'\n".format(module_name)) + cmd.load_all_symbols() + else: + cmd.load_module_symbols(module) + + # restore pagination state + gdb.execute("set pagination %s" % ("on" if pagination else "off")) + + return False + + +class LxSymbols(gdb.Command): + """(Re-)load symbols of Linux kernel and currently loaded modules. + +The kernel (vmlinux) is taken from the current working directly. Modules (.ko) +are scanned recursively, starting in the same directory. Optionally, the module +search path can be extended by a space separated list of paths passed to the +lx-symbols command.""" + + module_paths = [] + module_files = [] + module_files_updated = False + loaded_modules = [] + breakpoint = None + + def __init__(self): + super(LxSymbols, self).__init__("lx-symbols", gdb.COMMAND_FILES, + gdb.COMPLETE_FILENAME) + + def _update_module_files(self): + self.module_files = [] + for path in self.module_paths: + gdb.write("scanning for modules in {0}\n".format(path)) + for root, dirs, files in os.walk(path): + for name in files: + if name.endswith(".ko"): + self.module_files.append(root + "/" + name) + self.module_files_updated = True + + def _get_module_file(self, module_name): + module_pattern = ".*/{0}\.ko$".format( + module_name.replace("_", r"[_\-]")) + for name in self.module_files: + if re.match(module_pattern, name) and os.path.exists(name): + return name + return None + + def _section_arguments(self, module): + try: + sect_attrs = module['sect_attrs'].dereference() + except gdb.error: + return "" + attrs = sect_attrs['attrs'] + section_name_to_address = { + attrs[n]['name'].string() : attrs[n]['address'] + for n in range(int(sect_attrs['nsections']))} + args = [] + for section_name in [".data", ".data..read_mostly", ".rodata", ".bss"]: + address = section_name_to_address.get(section_name) + if address: + args.append(" -s {name} {addr}".format( + name=section_name, addr=str(address))) + return "".join(args) + + def load_module_symbols(self, module): + module_name = module['name'].string() + module_addr = str(module['module_core']).split()[0] + + module_file = self._get_module_file(module_name) + if not module_file and not self.module_files_updated: + self._update_module_files() + module_file = self._get_module_file(module_name) + + if module_file: + gdb.write("loading @{addr}: {filename}\n".format( + addr=module_addr, filename=module_file)) + cmdline = "add-symbol-file {filename} {addr}{sections}".format( + filename=module_file, + addr=module_addr, + sections=self._section_arguments(module)) + gdb.execute(cmdline, to_string=True) + if not module_name in self.loaded_modules: + self.loaded_modules.append(module_name) + else: + gdb.write("no module object found for '{0}'\n".format(module_name)) + + def load_all_symbols(self): + gdb.write("loading vmlinux\n") + + # Dropping symbols will disable all breakpoints. So save their states + # and restore them afterward. + saved_states = [] + if hasattr(gdb, 'breakpoints') and not gdb.breakpoints() is None: + for bp in gdb.breakpoints(): + saved_states.append({'breakpoint': bp, 'enabled': bp.enabled}) + + # drop all current symbols and reload vmlinux + gdb.execute("symbol-file", to_string=True) + gdb.execute("symbol-file vmlinux") + + self.loaded_modules = [] + module_list = modules.module_list() + if not module_list: + gdb.write("no modules found\n") + else: + [self.load_module_symbols(module) for module in module_list] + + for saved_state in saved_states: + saved_state['breakpoint'].enabled = saved_state['enabled'] + + def invoke(self, arg, from_tty): + self.module_paths = arg.split() + self.module_paths.append(os.getcwd()) + + # enforce update + self.module_files = [] + self.module_files_updated = False + + self.load_all_symbols() + + if hasattr(gdb, 'Breakpoint'): + if not self.breakpoint is None: + self.breakpoint.delete() + self.breakpoint = None + self.breakpoint = LoadModuleBreakpoint( + "kernel/module.c:do_init_module", self) + else: + gdb.write("Note: symbol update on module loading not supported " + "with this gdb version\n") + + +LxSymbols() diff --git a/scripts/gdb/linux/tasks.py b/scripts/gdb/linux/tasks.py new file mode 100644 index 000000000000..e2037d9bb7eb --- /dev/null +++ b/scripts/gdb/linux/tasks.py @@ -0,0 +1,100 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# task & thread tools +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + +from linux import utils + + +task_type = utils.CachedType("struct task_struct") + +def task_lists(): + global task_type + task_ptr_type = task_type.get_type().pointer() + init_task = gdb.parse_and_eval("init_task").address + t = g = init_task + + while True: + while True: + yield t + + t = utils.container_of(t['thread_group']['next'], + task_ptr_type, "thread_group") + if t == g: + break + + t = g = utils.container_of(g['tasks']['next'], + task_ptr_type, "tasks") + if t == init_task: + return + +def get_task_by_pid(pid): + for task in task_lists(): + if int(task['pid']) == pid: + return task + return None + + +class LxTaskByPidFunc(gdb.Function): + """Find Linux task by PID and return the task_struct variable. + +$lx_task_by_pid(PID): Given PID, iterate over all tasks of the target and +return that task_struct variable which PID matches.""" + + def __init__(self): + super(LxTaskByPidFunc, self).__init__("lx_task_by_pid") + + def invoke(self, pid): + task = get_task_by_pid(pid) + if task: + return task.dereference() + else: + raise gdb.GdbError("No task of PID " + str(pid)) + + +LxTaskByPidFunc() + + +thread_info_type = utils.CachedType("struct thread_info") + +ia64_task_size = None + + +def get_thread_info(task): + global thread_info_type + thread_info_ptr_type = thread_info_type.get_type().pointer() + if utils.is_target_arch("ia64"): + global ia64_task_size + if ia64_task_size is None: + ia64_task_size = gdb.parse_and_eval("sizeof(struct task_struct)") + thread_info_addr = task.address + ia64_task_size + thread_info = thread_info_addr.cast(thread_info_ptr_type) + else: + thread_info = task['stack'].cast(thread_info_ptr_type) + return thread_info.dereference() + + +class LxThreadInfoFunc (gdb.Function): + """Calculate Linux thread_info from task variable. + +$lx_thread_info(TASK): Given TASK, return the corresponding thread_info +variable.""" + + def __init__(self): + super(LxThreadInfoFunc, self).__init__("lx_thread_info") + + def invoke(self, task): + return get_thread_info(task) + + +LxThreadInfoFunc() diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py new file mode 100644 index 000000000000..128c306db3ee --- /dev/null +++ b/scripts/gdb/linux/utils.py @@ -0,0 +1,156 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# common utilities +# +# Copyright (c) Siemens AG, 2011-2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import gdb + + +class CachedType: + def __init__(self, name): + self._type = None + self._name = name + + def _new_objfile_handler(self, event): + self._type = None + gdb.events.new_objfile.disconnect(self._new_objfile_handler) + + def get_type(self): + if self._type is None: + self._type = gdb.lookup_type(self._name) + if self._type is None: + raise gdb.GdbError( + "cannot resolve type '{0}'".format(self._name)) + if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'): + gdb.events.new_objfile.connect(self._new_objfile_handler) + return self._type + + +long_type = CachedType("long") + + +def get_long_type(): + global long_type + return long_type.get_type() + + +def offset_of(typeobj, field): + element = gdb.Value(0).cast(typeobj) + return int(str(element[field].address).split()[0], 16) + + +def container_of(ptr, typeobj, member): + return (ptr.cast(get_long_type()) - + offset_of(typeobj, member)).cast(typeobj) + + +class ContainerOf(gdb.Function): + """Return pointer to containing data structure. + +$container_of(PTR, "TYPE", "ELEMENT"): Given PTR, return a pointer to the +data structure of the type TYPE in which PTR is the address of ELEMENT. +Note that TYPE and ELEMENT have to be quoted as strings.""" + + def __init__(self): + super(ContainerOf, self).__init__("container_of") + + def invoke(self, ptr, typename, elementname): + return container_of(ptr, gdb.lookup_type(typename.string()).pointer(), + elementname.string()) + +ContainerOf() + + +BIG_ENDIAN = 0 +LITTLE_ENDIAN = 1 +target_endianness = None + + +def get_target_endianness(): + global target_endianness + if target_endianness is None: + endian = gdb.execute("show endian", to_string=True) + if "little endian" in endian: + target_endianness = LITTLE_ENDIAN + elif "big endian" in endian: + target_endianness = BIG_ENDIAN + else: + raise gdb.GdgError("unknown endianness '{0}'".format(str(endian))) + return target_endianness + + +def read_u16(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return ord(buffer[0]) + (ord(buffer[1]) << 8) + else: + return ord(buffer[1]) + (ord(buffer[0]) << 8) + + +def read_u32(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return read_u16(buffer[0:2]) + (read_u16(buffer[2:4]) << 16) + else: + return read_u16(buffer[2:4]) + (read_u16(buffer[0:2]) << 16) + + +def read_u64(buffer): + if get_target_endianness() == LITTLE_ENDIAN: + return read_u32(buffer[0:4]) + (read_u32(buffer[4:8]) << 32) + else: + return read_u32(buffer[4:8]) + (read_u32(buffer[0:4]) << 32) + + +target_arch = None + + +def is_target_arch(arch): + if hasattr(gdb.Frame, 'architecture'): + return arch in gdb.newest_frame().architecture().name() + else: + global target_arch + if target_arch is None: + target_arch = gdb.execute("show architecture", to_string=True) + return arch in target_arch + + +GDBSERVER_QEMU = 0 +GDBSERVER_KGDB = 1 +gdbserver_type = None + + +def get_gdbserver_type(): + def exit_handler(event): + global gdbserver_type + gdbserver_type = None + gdb.events.exited.disconnect(exit_handler) + + def probe_qemu(): + try: + return gdb.execute("monitor info version", to_string=True) != "" + except: + return False + + def probe_kgdb(): + try: + thread_info = gdb.execute("info thread 2", to_string=True) + return "shadowCPU0" in thread_info + except: + return False + + global gdbserver_type + if gdbserver_type is None: + if probe_qemu(): + gdbserver_type = GDBSERVER_QEMU + elif probe_kgdb(): + gdbserver_type = GDBSERVER_KGDB + if not gdbserver_type is None and hasattr(gdb, 'events'): + gdb.events.exited.connect(exit_handler) + return gdbserver_type diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py new file mode 100644 index 000000000000..48489285f119 --- /dev/null +++ b/scripts/gdb/vmlinux-gdb.py @@ -0,0 +1,30 @@ +# +# gdb helper commands and functions for Linux kernel debugging +# +# loader module +# +# Copyright (c) Siemens AG, 2012, 2013 +# +# Authors: +# Jan Kiszka <jan.kiszka@siemens.com> +# +# This work is licensed under the terms of the GNU GPL version 2. +# + +import os + +sys.path.insert(0, os.path.dirname(__file__) + "/scripts/gdb") + +try: + gdb.parse_and_eval("0") + gdb.execute("", to_string=True) +except: + gdb.write("NOTE: gdb 7.2 or later required for Linux helper scripts to " + "work.\n") +else: + import linux.utils + import linux.symbols + import linux.modules + import linux.dmesg + import linux.tasks + import linux.cpus |