linux/arch/powerpc/platforms/cell/spufs/coredump.c
<<
>>
Prefs
   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/fdtable.h>
  26#include <linux/fs.h>
  27#include <linux/gfp.h>
  28#include <linux/list.h>
  29#include <linux/syscalls.h>
  30#include <linux/coredump.h>
  31#include <linux/binfmts.h>
  32
  33#include <linux/uaccess.h>
  34
  35#include "spufs.h"
  36
  37static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer,
  38                                size_t size, loff_t *off)
  39{
  40        u64 data;
  41        int ret;
  42
  43        if (spufs_coredump_read[num].read)
  44                return spufs_coredump_read[num].read(ctx, buffer, size, off);
  45
  46        data = spufs_coredump_read[num].get(ctx);
  47        ret = snprintf(buffer, size, "0x%.16llx", data);
  48        if (ret >= size)
  49                return size;
  50        return ++ret; /* count trailing NULL */
  51}
  52
  53static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
  54{
  55        int i, sz, total = 0;
  56        char *name;
  57        char fullname[80];
  58
  59        for (i = 0; spufs_coredump_read[i].name != NULL; i++) {
  60                name = spufs_coredump_read[i].name;
  61                sz = spufs_coredump_read[i].size;
  62
  63                sprintf(fullname, "SPU/%d/%s", dfd, name);
  64
  65                total += sizeof(struct elf_note);
  66                total += roundup(strlen(fullname) + 1, 4);
  67                total += roundup(sz, 4);
  68        }
  69
  70        return total;
  71}
  72
  73static int match_context(const void *v, struct file *file, unsigned fd)
  74{
  75        struct spu_context *ctx;
  76        if (file->f_op != &spufs_context_fops)
  77                return 0;
  78        ctx = SPUFS_I(file_inode(file))->i_ctx;
  79        if (ctx->flags & SPU_CREATE_NOSCHED)
  80                return 0;
  81        return fd + 1;
  82}
  83
  84/*
  85 * The additional architecture-specific notes for Cell are various
  86 * context files in the spu context.
  87 *
  88 * This function iterates over all open file descriptors and sees
  89 * if they are a directory in spufs.  In that case we use spufs
  90 * internal functionality to dump them without needing to actually
  91 * open the files.
  92 */
  93/*
  94 * descriptor table is not shared, so files can't change or go away.
  95 */
  96static struct spu_context *coredump_next_context(int *fd)
  97{
  98        struct file *file;
  99        int n = iterate_fd(current->files, *fd, match_context, NULL);
 100        if (!n)
 101                return NULL;
 102        *fd = n - 1;
 103        file = fcheck(*fd);
 104        return SPUFS_I(file_inode(file))->i_ctx;
 105}
 106
 107int spufs_coredump_extra_notes_size(void)
 108{
 109        struct spu_context *ctx;
 110        int size = 0, rc, fd;
 111
 112        fd = 0;
 113        while ((ctx = coredump_next_context(&fd)) != NULL) {
 114                rc = spu_acquire_saved(ctx);
 115                if (rc)
 116                        break;
 117                rc = spufs_ctx_note_size(ctx, fd);
 118                spu_release_saved(ctx);
 119                if (rc < 0)
 120                        break;
 121
 122                size += rc;
 123
 124                /* start searching the next fd next time */
 125                fd++;
 126        }
 127
 128        return size;
 129}
 130
 131static int spufs_arch_write_note(struct spu_context *ctx, int i,
 132                                  struct coredump_params *cprm, int dfd)
 133{
 134        loff_t pos = 0;
 135        int sz, rc, total = 0;
 136        const int bufsz = PAGE_SIZE;
 137        char *name;
 138        char fullname[80], *buf;
 139        struct elf_note en;
 140        size_t skip;
 141
 142        buf = (void *)get_zeroed_page(GFP_KERNEL);
 143        if (!buf)
 144                return -ENOMEM;
 145
 146        name = spufs_coredump_read[i].name;
 147        sz = spufs_coredump_read[i].size;
 148
 149        sprintf(fullname, "SPU/%d/%s", dfd, name);
 150        en.n_namesz = strlen(fullname) + 1;
 151        en.n_descsz = sz;
 152        en.n_type = NT_SPU;
 153
 154        if (!dump_emit(cprm, &en, sizeof(en)))
 155                goto Eio;
 156
 157        if (!dump_emit(cprm, fullname, en.n_namesz))
 158                goto Eio;
 159
 160        if (!dump_align(cprm, 4))
 161                goto Eio;
 162
 163        do {
 164                rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
 165                if (rc > 0) {
 166                        if (!dump_emit(cprm, buf, rc))
 167                                goto Eio;
 168                        total += rc;
 169                }
 170        } while (rc == bufsz && total < sz);
 171
 172        if (rc < 0)
 173                goto out;
 174
 175        skip = roundup(cprm->pos - total + sz, 4) - cprm->pos;
 176        if (!dump_skip(cprm, skip))
 177                goto Eio;
 178
 179        rc = 0;
 180out:
 181        free_page((unsigned long)buf);
 182        return rc;
 183Eio:
 184        free_page((unsigned long)buf);
 185        return -EIO;
 186}
 187
 188int spufs_coredump_extra_notes_write(struct coredump_params *cprm)
 189{
 190        struct spu_context *ctx;
 191        int fd, j, rc;
 192
 193        fd = 0;
 194        while ((ctx = coredump_next_context(&fd)) != NULL) {
 195                rc = spu_acquire_saved(ctx);
 196                if (rc)
 197                        return rc;
 198
 199                for (j = 0; spufs_coredump_read[j].name != NULL; j++) {
 200                        rc = spufs_arch_write_note(ctx, j, cprm, fd);
 201                        if (rc) {
 202                                spu_release_saved(ctx);
 203                                return rc;
 204                        }
 205                }
 206
 207                spu_release_saved(ctx);
 208
 209                /* start searching the next fd next time */
 210                fd++;
 211        }
 212
 213        return 0;
 214}
 215