Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
coredump.c
Go to the documentation of this file.
1 /*
2  * SPU core dump code
3  *
4  * (C) Copyright 2006 IBM Corp.
5  *
6  * Author: Dwayne Grant McConnell <[email protected]>
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 
31 #include <asm/uaccess.h>
32 
33 #include "spufs.h"
34 
35 static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer,
36  size_t size, loff_t *off)
37 {
38  u64 data;
39  int ret;
40 
41  if (spufs_coredump_read[num].read)
42  return spufs_coredump_read[num].read(ctx, buffer, size, off);
43 
44  data = spufs_coredump_read[num].get(ctx);
45  ret = snprintf(buffer, size, "0x%.16llx", data);
46  if (ret >= size)
47  return size;
48  return ++ret; /* count trailing NULL */
49 }
50 
51 /*
52  * These are the only things you should do on a core-file: use only these
53  * functions to write out all the necessary info.
54  */
55 static int spufs_dump_write(struct file *file, const void *addr, int nr, loff_t *foffset)
56 {
57  unsigned long limit = rlimit(RLIMIT_CORE);
58  ssize_t written;
59 
60  if (*foffset + nr > limit)
61  return -EIO;
62 
63  written = file->f_op->write(file, addr, nr, &file->f_pos);
64  *foffset += written;
65 
66  if (written != nr)
67  return -EIO;
68 
69  return 0;
70 }
71 
72 static int spufs_dump_align(struct file *file, char *buf, loff_t new_off,
73  loff_t *foffset)
74 {
75  int rc, size;
76 
77  size = min((loff_t)PAGE_SIZE, new_off - *foffset);
78  memset(buf, 0, size);
79 
80  rc = 0;
81  while (rc == 0 && new_off > *foffset) {
82  size = min((loff_t)PAGE_SIZE, new_off - *foffset);
83  rc = spufs_dump_write(file, buf, size, foffset);
84  }
85 
86  return rc;
87 }
88 
89 static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
90 {
91  int i, sz, total = 0;
92  char *name;
93  char fullname[80];
94 
95  for (i = 0; spufs_coredump_read[i].name != NULL; 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  total += roundup(sz, 4);
104  }
105 
106  return total;
107 }
108 
109 static int match_context(const void *v, struct file *file, unsigned fd)
110 {
111  struct spu_context *ctx;
112  if (file->f_op != &spufs_context_fops)
113  return 0;
114  ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx;
115  if (ctx->flags & SPU_CREATE_NOSCHED)
116  return 0;
117  return fd + 1;
118 }
119 
120 /*
121  * The additional architecture-specific notes for Cell are various
122  * context files in the spu context.
123  *
124  * This function iterates over all open file descriptors and sees
125  * if they are a directory in spufs. In that case we use spufs
126  * internal functionality to dump them without needing to actually
127  * open the files.
128  */
129 /*
130  * descriptor table is not shared, so files can't change or go away.
131  */
132 static struct spu_context *coredump_next_context(int *fd)
133 {
134  struct file *file;
135  int n = iterate_fd(current->files, *fd, match_context, NULL);
136  if (!n)
137  return NULL;
138  *fd = n - 1;
139  file = fcheck(*fd);
140  return SPUFS_I(file->f_dentry->d_inode)->i_ctx;
141 }
142 
144 {
145  struct spu_context *ctx;
146  int size = 0, rc, fd;
147 
148  fd = 0;
149  while ((ctx = coredump_next_context(&fd)) != NULL) {
150  rc = spu_acquire_saved(ctx);
151  if (rc)
152  break;
153  rc = spufs_ctx_note_size(ctx, fd);
154  spu_release_saved(ctx);
155  if (rc < 0)
156  break;
157 
158  size += rc;
159 
160  /* start searching the next fd next time */
161  fd++;
162  }
163 
164  return size;
165 }
166 
167 static int spufs_arch_write_note(struct spu_context *ctx, int i,
168  struct file *file, int dfd, loff_t *foffset)
169 {
170  loff_t pos = 0;
171  int sz, rc, nread, total = 0;
172  const int bufsz = PAGE_SIZE;
173  char *name;
174  char fullname[80], *buf;
175  struct elf_note en;
176 
177  buf = (void *)get_zeroed_page(GFP_KERNEL);
178  if (!buf)
179  return -ENOMEM;
180 
181  name = spufs_coredump_read[i].name;
182  sz = spufs_coredump_read[i].size;
183 
184  sprintf(fullname, "SPU/%d/%s", dfd, name);
185  en.n_namesz = strlen(fullname) + 1;
186  en.n_descsz = sz;
187  en.n_type = NT_SPU;
188 
189  rc = spufs_dump_write(file, &en, sizeof(en), foffset);
190  if (rc)
191  goto out;
192 
193  rc = spufs_dump_write(file, fullname, en.n_namesz, foffset);
194  if (rc)
195  goto out;
196 
197  rc = spufs_dump_align(file, buf, roundup(*foffset, 4), foffset);
198  if (rc)
199  goto out;
200 
201  do {
202  nread = do_coredump_read(i, ctx, buf, bufsz, &pos);
203  if (nread > 0) {
204  rc = spufs_dump_write(file, buf, nread, foffset);
205  if (rc)
206  goto out;
207  total += nread;
208  }
209  } while (nread == bufsz && total < sz);
210 
211  if (nread < 0) {
212  rc = nread;
213  goto out;
214  }
215 
216  rc = spufs_dump_align(file, buf, roundup(*foffset - total + sz, 4),
217  foffset);
218 
219 out:
220  free_page((unsigned long)buf);
221  return rc;
222 }
223 
224 int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset)
225 {
226  struct spu_context *ctx;
227  int fd, j, rc;
228 
229  fd = 0;
230  while ((ctx = coredump_next_context(&fd)) != NULL) {
231  rc = spu_acquire_saved(ctx);
232  if (rc)
233  return rc;
234 
235  for (j = 0; spufs_coredump_read[j].name != NULL; j++) {
236  rc = spufs_arch_write_note(ctx, j, file, fd, foffset);
237  if (rc) {
238  spu_release_saved(ctx);
239  return rc;
240  }
241  }
242 
243  spu_release_saved(ctx);
244 
245  /* start searching the next fd next time */
246  fd++;
247  }
248 
249  return 0;
250 }