Pull bugfix into test branch
[linux-2.6] / arch / powerpc / platforms / cell / spufs / coredump.c
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_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 };