]> err.no Git - linux-2.6/blob - arch/powerpc/platforms/cell/spufs/coredump.c
99f8e0b0089724b3e8f057ed7bdc9162c231f972
[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 spu_context *ctx, int dfd)
113 {
114         struct spufs_ctx_info *ctx_info;
115         int size;
116
117         ctx_info = kzalloc(sizeof(*ctx_info), GFP_KERNEL);
118         if (unlikely(!ctx_info))
119                 return -ENOMEM;
120
121         ctx_info->dfd = dfd;
122         ctx_info->ctx = ctx;
123
124         spufs_fill_memsize(ctx_info);
125
126         size = spufs_ctx_note_size(ctx_info);
127         list_add(&ctx_info->list, &ctx_info_list);
128         return size;
129 }
130
131 /*
132  * The additional architecture-specific notes for Cell are various
133  * context files in the spu context.
134  *
135  * This function iterates over all open file descriptors and sees
136  * if they are a directory in spufs.  In that case we use spufs
137  * internal functionality to dump them without needing to actually
138  * open the files.
139  */
140 static struct spu_context *coredump_next_context(int *fd)
141 {
142         struct fdtable *fdt = files_fdtable(current->files);
143         struct file *file;
144         struct spu_context *ctx = NULL;
145
146         for (; *fd < fdt->max_fds; (*fd)++) {
147                 if (!FD_ISSET(*fd, fdt->open_fds))
148                         continue;
149
150                 file = fcheck(*fd);
151
152                 if (!file || file->f_op != &spufs_context_fops)
153                         continue;
154
155                 ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx;
156                 if (ctx->flags & SPU_CREATE_NOSCHED)
157                         continue;
158
159                 /* start searching the next fd next time we're called */
160                 (*fd)++;
161                 break;
162         }
163
164         return ctx;
165 }
166
167 static int spufs_arch_notes_size(void)
168 {
169         struct spu_context *ctx;
170         int size = 0, rc, fd;
171
172         fd = 0;
173         while ((ctx = coredump_next_context(&fd)) != NULL) {
174                 rc = spufs_add_one_context(ctx, fd);
175                 if (rc < 0)
176                         break;
177
178                 size += rc;
179         }
180
181         return size;
182 }
183
184 static void spufs_arch_write_note(struct spufs_ctx_info *ctx_info, int i,
185                                 struct file *file)
186 {
187         struct spu_context *ctx;
188         loff_t pos = 0;
189         int sz, dfd, rc, total = 0;
190         const int bufsz = PAGE_SIZE;
191         char *name;
192         char fullname[80], *buf;
193         struct elf_note en;
194
195         buf = (void *)get_zeroed_page(GFP_KERNEL);
196         if (!buf)
197                 return;
198
199         dfd = ctx_info->dfd;
200         name = spufs_coredump_read[i].name;
201
202         if (!strcmp(name, "mem"))
203                 sz = ctx_info->memsize;
204         else
205                 sz = spufs_coredump_read[i].size;
206
207         ctx = ctx_info->ctx;
208         if (!ctx)
209                 goto out;
210
211         sprintf(fullname, "SPU/%d/%s", dfd, name);
212         en.n_namesz = strlen(fullname) + 1;
213         en.n_descsz = sz;
214         en.n_type = NT_SPU;
215
216         if (!spufs_dump_write(file, &en, sizeof(en)))
217                 goto out;
218         if (!spufs_dump_write(file, fullname, en.n_namesz))
219                 goto out;
220         if (!spufs_dump_seek(file, roundup((unsigned long)file->f_pos, 4)))
221                 goto out;
222
223         do {
224                 rc = do_coredump_read(i, ctx, buf, bufsz, &pos);
225                 if (rc > 0) {
226                         if (!spufs_dump_write(file, buf, rc))
227                                 goto out;
228                         total += rc;
229                 }
230         } while (rc == bufsz && total < sz);
231
232         spufs_dump_seek(file, roundup((unsigned long)file->f_pos
233                                                 - total + sz, 4));
234 out:
235         free_page((unsigned long)buf);
236 }
237
238 static void spufs_arch_write_notes(struct file *file)
239 {
240         int j;
241         struct spufs_ctx_info *ctx_info, *next;
242
243         list_for_each_entry_safe(ctx_info, next, &ctx_info_list, list) {
244                 spu_acquire_saved(ctx_info->ctx);
245                 for (j = 0; j < spufs_coredump_num_notes; j++)
246                         spufs_arch_write_note(ctx_info, j, file);
247                 spu_release_saved(ctx_info->ctx);
248                 list_del(&ctx_info->list);
249                 kfree(ctx_info);
250         }
251 }
252
253 struct spu_coredump_calls spufs_coredump_calls = {
254         .arch_notes_size = spufs_arch_notes_size,
255         .arch_write_notes = spufs_arch_write_notes,
256         .owner = THIS_MODULE,
257 };