Dwayne Grant McConnell | bf1ab97 | 2006-11-23 00:46:37 +0100 | [diff] [blame^] | 1 | /* |
| 2 | * SPU core dump code |
| 3 | * |
| 4 | * (C) Copyright 2006 IBM Corp. |
| 5 | * |
| 6 | * Author: Dwayne Grant McConnell <decimal@us.ibm.com> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2, or (at your option) |
| 11 | * any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, write to the Free Software |
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 21 | */ |
| 22 | |
| 23 | #include <linux/elf.h> |
| 24 | #include <linux/file.h> |
| 25 | #include <linux/fs.h> |
| 26 | #include <linux/list.h> |
| 27 | #include <linux/module.h> |
| 28 | #include <linux/syscalls.h> |
| 29 | |
| 30 | #include <asm/uaccess.h> |
| 31 | |
| 32 | #include "spufs.h" |
| 33 | |
| 34 | struct spufs_ctx_info { |
| 35 | struct list_head list; |
| 36 | int dfd; |
| 37 | int memsize; /* in bytes */ |
| 38 | struct spu_context *ctx; |
| 39 | }; |
| 40 | |
| 41 | static LIST_HEAD(ctx_info_list); |
| 42 | |
| 43 | static ssize_t do_coredump_read(int num, struct spu_context *ctx, void __user *buffer, |
| 44 | size_t size, loff_t *off) |
| 45 | { |
| 46 | u64 data; |
| 47 | int ret; |
| 48 | |
| 49 | if (spufs_coredump_read[num].read) |
| 50 | return spufs_coredump_read[num].read(ctx, buffer, size, off); |
| 51 | |
| 52 | data = spufs_coredump_read[num].get(ctx); |
| 53 | ret = copy_to_user(buffer, &data, 8); |
| 54 | return ret ? -EFAULT : 8; |
| 55 | } |
| 56 | |
| 57 | /* |
| 58 | * These are the only things you should do on a core-file: use only these |
| 59 | * functions to write out all the necessary info. |
| 60 | */ |
| 61 | static int spufs_dump_write(struct file *file, const void *addr, int nr) |
| 62 | { |
| 63 | return file->f_op->write(file, addr, nr, &file->f_pos) == nr; |
| 64 | } |
| 65 | |
| 66 | static int spufs_dump_seek(struct file *file, loff_t off) |
| 67 | { |
| 68 | if (file->f_op->llseek) { |
| 69 | if (file->f_op->llseek(file, off, 0) != off) |
| 70 | return 0; |
| 71 | } else |
| 72 | file->f_pos = off; |
| 73 | return 1; |
| 74 | } |
| 75 | |
| 76 | static void spufs_fill_memsize(struct spufs_ctx_info *ctx_info) |
| 77 | { |
| 78 | struct spu_context *ctx; |
| 79 | unsigned long long lslr; |
| 80 | |
| 81 | ctx = ctx_info->ctx; |
| 82 | lslr = ctx->csa.priv2.spu_lslr_RW; |
| 83 | ctx_info->memsize = lslr + 1; |
| 84 | } |
| 85 | |
| 86 | static int spufs_ctx_note_size(struct spufs_ctx_info *ctx_info) |
| 87 | { |
| 88 | int dfd, memsize, i, sz, total = 0; |
| 89 | char *name; |
| 90 | char fullname[80]; |
| 91 | |
| 92 | dfd = ctx_info->dfd; |
| 93 | memsize = ctx_info->memsize; |
| 94 | |
| 95 | for (i = 0; spufs_coredump_read[i].name; i++) { |
| 96 | name = spufs_coredump_read[i].name; |
| 97 | sz = spufs_coredump_read[i].size; |
| 98 | |
| 99 | sprintf(fullname, "SPU/%d/%s", dfd, name); |
| 100 | |
| 101 | total += sizeof(struct elf_note); |
| 102 | total += roundup(strlen(fullname) + 1, 4); |
| 103 | if (!strcmp(name, "mem")) |
| 104 | total += roundup(memsize, 4); |
| 105 | else |
| 106 | total += roundup(sz, 4); |
| 107 | } |
| 108 | |
| 109 | return total; |
| 110 | } |
| 111 | |
| 112 | static int spufs_add_one_context(struct file *file, int dfd) |
| 113 | { |
| 114 | struct spu_context *ctx; |
| 115 | struct spufs_ctx_info *ctx_info; |
| 116 | int size; |
| 117 | |
| 118 | ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx; |
| 119 | if (ctx->flags & SPU_CREATE_NOSCHED) |
| 120 | return 0; |
| 121 | |
| 122 | ctx_info = kzalloc(sizeof(*ctx_info), GFP_KERNEL); |
| 123 | if (unlikely(!ctx_info)) |
| 124 | return -ENOMEM; |
| 125 | |
| 126 | ctx_info->dfd = dfd; |
| 127 | ctx_info->ctx = ctx; |
| 128 | |
| 129 | spufs_fill_memsize(ctx_info); |
| 130 | |
| 131 | size = spufs_ctx_note_size(ctx_info); |
| 132 | list_add(&ctx_info->list, &ctx_info_list); |
| 133 | return size; |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | * The additional architecture-specific notes for Cell are various |
| 138 | * context files in the spu context. |
| 139 | * |
| 140 | * This function iterates over all open file descriptors and sees |
| 141 | * if they are a directory in spufs. In that case we use spufs |
| 142 | * internal functionality to dump them without needing to actually |
| 143 | * open the files. |
| 144 | */ |
| 145 | static int spufs_arch_notes_size(void) |
| 146 | { |
| 147 | struct fdtable *fdt = files_fdtable(current->files); |
| 148 | int size = 0, fd; |
| 149 | |
| 150 | for (fd = 0; fd < fdt->max_fdset && fd < fdt->max_fds; fd++) { |
| 151 | if (FD_ISSET(fd, fdt->open_fds)) { |
| 152 | struct file *file = fcheck(fd); |
| 153 | |
| 154 | if (file && file->f_op == &spufs_context_fops) { |
| 155 | int rval = spufs_add_one_context(file, fd); |
| 156 | if (rval < 0) |
| 157 | break; |
| 158 | size += rval; |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | return size; |
| 164 | } |
| 165 | |
| 166 | static void spufs_arch_write_note(struct spufs_ctx_info *ctx_info, int i, |
| 167 | struct file *file) |
| 168 | { |
| 169 | struct spu_context *ctx; |
| 170 | loff_t pos = 0; |
| 171 | int sz, dfd, rc, total = 0; |
| 172 | const int bufsz = 4096; |
| 173 | char *name; |
| 174 | char fullname[80], *buf; |
| 175 | struct elf_note en; |
| 176 | |
| 177 | buf = kmalloc(bufsz, GFP_KERNEL); |
| 178 | if (!buf) |
| 179 | return; |
| 180 | |
| 181 | dfd = ctx_info->dfd; |
| 182 | name = spufs_coredump_read[i].name; |
| 183 | |
| 184 | if (!strcmp(name, "mem")) |
| 185 | sz = ctx_info->memsize; |
| 186 | else |
| 187 | sz = spufs_coredump_read[i].size; |
| 188 | |
| 189 | ctx = ctx_info->ctx; |
| 190 | if (!ctx) { |
| 191 | return; |
| 192 | } |
| 193 | |
| 194 | sprintf(fullname, "SPU/%d/%s", dfd, name); |
| 195 | en.n_namesz = strlen(fullname) + 1; |
| 196 | en.n_descsz = sz; |
| 197 | en.n_type = NT_SPU; |
| 198 | |
| 199 | if (!spufs_dump_write(file, &en, sizeof(en))) |
| 200 | return; |
| 201 | if (!spufs_dump_write(file, fullname, en.n_namesz)) |
| 202 | return; |
| 203 | if (!spufs_dump_seek(file, roundup((unsigned long)file->f_pos, 4))) |
| 204 | return; |
| 205 | |
| 206 | do { |
| 207 | rc = do_coredump_read(i, ctx, buf, bufsz, &pos); |
| 208 | if (rc > 0) { |
| 209 | if (!spufs_dump_write(file, buf, rc)) |
| 210 | return; |
| 211 | total += rc; |
| 212 | } |
| 213 | } while (rc == bufsz && total < sz); |
| 214 | |
| 215 | spufs_dump_seek(file, roundup((unsigned long)file->f_pos |
| 216 | - total + sz, 4)); |
| 217 | } |
| 218 | |
| 219 | static void spufs_arch_write_notes(struct file *file) |
| 220 | { |
| 221 | int j; |
| 222 | struct spufs_ctx_info *ctx_info, *next; |
| 223 | |
| 224 | list_for_each_entry_safe(ctx_info, next, &ctx_info_list, list) { |
| 225 | spu_acquire_saved(ctx_info->ctx); |
| 226 | for (j = 0; j < spufs_coredump_num_notes; j++) |
| 227 | spufs_arch_write_note(ctx_info, j, file); |
| 228 | spu_release(ctx_info->ctx); |
| 229 | list_del(&ctx_info->list); |
| 230 | kfree(ctx_info); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | struct spu_coredump_calls spufs_coredump_calls = { |
| 235 | .arch_notes_size = spufs_arch_notes_size, |
| 236 | .arch_write_notes = spufs_arch_write_notes, |
| 237 | .owner = THIS_MODULE, |
| 238 | }; |