Whamcloud - gitweb
517035cdbcd2a7df60b7ea019fd607c415aeb3ac
[fs/lustre-release.git] / lustre / obdclass / linux / linux-module.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Object Devices Class Driver
5  *
6  *  Copyright (C) 2001-2003 Cluster File Systems, Inc.
7  *
8  *   This file is part of Lustre, http://www.lustre.org.
9  *
10  *   Lustre is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   Lustre is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with Lustre; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * These are the only exported functions, they provide some generic
24  * infrastructure for managing object devices
25  */
26 #define DEBUG_SUBSYSTEM S_CLASS
27 #ifndef EXPORT_SYMTAB
28 # define EXPORT_SYMTAB
29 #endif
30
31 #ifdef __KERNEL__
32 #include <linux/config.h> /* for CONFIG_PROC_FS */
33 #include <linux/module.h>
34 #include <linux/errno.h>
35 #include <linux/kernel.h>
36 #include <linux/major.h>
37 #include <linux/sched.h>
38 #include <linux/lp.h>
39 #include <linux/slab.h>
40 #include <linux/ioport.h>
41 #include <linux/fcntl.h>
42 #include <linux/delay.h>
43 #include <linux/skbuff.h>
44 #include <linux/proc_fs.h>
45 #include <linux/fs.h>
46 #include <linux/poll.h>
47 #include <linux/init.h>
48 #include <linux/list.h>
49 #include <linux/highmem.h>
50 #include <asm/io.h>
51 #include <asm/ioctls.h>
52 #include <asm/system.h>
53 #include <asm/poll.h>
54 #include <asm/uaccess.h>
55 #include <linux/miscdevice.h>
56 #include <linux/smp_lock.h>
57 #include <linux/seq_file.h>
58 #else
59 # include <liblustre.h>
60 #endif
61
62 #include <libcfs/libcfs.h>
63 #include <obd_support.h>
64 #include <obd_class.h>
65 #include <lprocfs_status.h>
66 #ifdef __KERNEL__
67 #include <linux/lustre_build_version.h>
68 #include <linux/lustre_version.h>
69
70 int proc_version;
71
72 /* buffer MUST be at least the size of obd_ioctl_hdr */
73 int obd_ioctl_getdata(char **buf, int *len, void *arg)
74 {
75         struct obd_ioctl_hdr hdr;
76         struct obd_ioctl_data *data;
77         int err;
78         int offset = 0;
79         ENTRY;
80
81         err = copy_from_user(&hdr, (void *)arg, sizeof(hdr));
82         if ( err ) 
83                 RETURN(err);
84
85         if (hdr.ioc_version != OBD_IOCTL_VERSION) {
86                 CERROR("Version mismatch kernel vs application\n");
87                 RETURN(-EINVAL);
88         }
89
90         if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) {
91                 CERROR("User buffer len %d exceeds %d max buffer\n",
92                        hdr.ioc_len, OBD_MAX_IOCTL_BUFFER);
93                 RETURN(-EINVAL);
94         }
95
96         if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) {
97                 CERROR("User buffer too small for ioctl (%d)\n", hdr.ioc_len);
98                 RETURN(-EINVAL);
99         }
100
101         /* XXX allocate this more intelligently, using kmalloc when
102          * appropriate */
103         OBD_VMALLOC(*buf, hdr.ioc_len);
104         if (*buf == NULL) {
105                 CERROR("Cannot allocate control buffer of len %d\n",
106                        hdr.ioc_len);
107                 RETURN(-EINVAL);
108         }
109         *len = hdr.ioc_len;
110         data = (struct obd_ioctl_data *)*buf;
111
112         err = copy_from_user(*buf, (void *)arg, hdr.ioc_len);
113         if ( err ) {
114                 OBD_VFREE(*buf, hdr.ioc_len);
115                 RETURN(err);
116         }
117
118         if (obd_ioctl_is_invalid(data)) {
119                 CERROR("ioctl not correctly formatted\n");
120                 OBD_VFREE(*buf, hdr.ioc_len);
121                 RETURN(-EINVAL);
122         }
123
124         if (data->ioc_inllen1) {
125                 data->ioc_inlbuf1 = &data->ioc_bulk[0];
126                 offset += size_round(data->ioc_inllen1);
127         }
128
129         if (data->ioc_inllen2) {
130                 data->ioc_inlbuf2 = &data->ioc_bulk[0] + offset;
131                 offset += size_round(data->ioc_inllen2);
132         }
133
134         if (data->ioc_inllen3) {
135                 data->ioc_inlbuf3 = &data->ioc_bulk[0] + offset;
136                 offset += size_round(data->ioc_inllen3);
137         }
138
139         if (data->ioc_inllen4) {
140                 data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
141         }
142
143         EXIT;
144         return 0;
145 }
146
147 int obd_ioctl_popdata(void *arg, void *data, int len)
148 {
149         int err; 
150         
151         err = copy_to_user(arg, data, len);
152         if (err)
153                 err = -EFAULT;
154         return err;
155 }
156
157 EXPORT_SYMBOL(obd_ioctl_getdata);
158 EXPORT_SYMBOL(obd_ioctl_popdata);
159
160 #define OBD_MINOR 241
161 extern struct cfs_psdev_ops          obd_psdev_ops;
162
163 /*  opening /dev/obd */
164 static int obd_class_open(struct inode * inode, struct file * file)
165 {
166         if (obd_psdev_ops.p_open != NULL)
167                 return obd_psdev_ops.p_open(0, NULL);
168         return -EPERM;
169 }
170
171 /*  closing /dev/obd */
172 static int obd_class_release(struct inode * inode, struct file * file)
173 {
174         if (obd_psdev_ops.p_close != NULL)
175                 return obd_psdev_ops.p_close(0, NULL);
176         return -EPERM;
177 }
178
179 /* to control /dev/obd */
180 static int obd_class_ioctl(struct inode *inode, struct file *filp,
181                            unsigned int cmd, unsigned long arg)
182 {
183         int err = 0;
184         ENTRY;
185
186         if (current->fsuid != 0)
187                 RETURN(err = -EACCES);
188         if ((cmd & 0xffffff00) == ((int)'T') << 8) /* ignore all tty ioctls */
189                 RETURN(err = -ENOTTY);
190
191         if (obd_psdev_ops.p_ioctl != NULL)
192                 err = obd_psdev_ops.p_ioctl(NULL, cmd, (void *)arg);
193         else
194                 err = -EPERM;
195
196         RETURN(err);
197 }
198
199 /* declare character device */
200 static struct file_operations obd_psdev_fops = {
201         .owner   = THIS_MODULE,
202         .ioctl   = obd_class_ioctl,     /* ioctl */
203         .open    = obd_class_open,      /* open */
204         .release = obd_class_release,   /* release */
205 };
206
207 /* modules setup */
208 cfs_psdev_t obd_psdev = {
209         .minor = OBD_MINOR,
210         .name  = "obd_psdev",
211         .fops  = &obd_psdev_fops,
212 };
213
214 #endif
215
216 #ifdef LPROCFS
217 int obd_proc_read_version(char *page, char **start, off_t off, int count,
218                           int *eof, void *data)
219 {
220         *eof = 1;
221         return snprintf(page, count, "%s\n", BUILD_VERSION);
222 }
223
224 int obd_proc_read_kernel_version(char *page, char **start, off_t off, int count,
225                                  int *eof, void *data)
226 {
227         *eof = 1;
228         return snprintf(page, count, "%u\n", LUSTRE_KERNEL_VERSION);
229 }
230
231 int obd_proc_read_pinger(char *page, char **start, off_t off, int count,
232                          int *eof, void *data)
233 {
234         *eof = 1;
235         return snprintf(page, count, "%s\n",
236 #ifdef ENABLE_PINGER
237                         "on"
238 #else
239                         "off"
240 #endif
241                        );
242 }
243
244 static int obd_proc_read_health(char *page, char **start, off_t off,
245                                 int count, int *eof, void *data)
246 {
247         int rc = 0, i;
248         *eof = 1;
249
250         if (libcfs_catastrophe)
251                 rc += snprintf(page + rc, count - rc, "LBUG\n");
252
253         spin_lock(&obd_dev_lock);
254         for (i = 0; i < MAX_OBD_DEVICES; i++) {
255                 struct obd_device *obd;
256
257                 obd = &obd_dev[i];
258                 if (obd->obd_type == NULL)
259                         continue;
260
261                 class_incref(obd);
262                 spin_unlock(&obd_dev_lock);
263
264                 if (obd_health_check(obd)) {
265                         rc += snprintf(page + rc, count - rc,
266                                        "device %s reported unhealthy\n",
267                                        obd->obd_name);
268                 }
269                 class_decref(obd);
270                 spin_lock(&obd_dev_lock);
271         }
272         spin_unlock(&obd_dev_lock);
273
274         if (rc == 0)
275                 return snprintf(page, count, "healthy\n");
276
277         rc += snprintf(page + rc, count - rc, "NOT HEALTHY\n");
278         return rc;
279 }
280
281 static int obd_proc_rd_health_timeout(char *page, char **start, off_t off,
282                                       int count, int *eof, void *data)
283 {
284         *eof = 1;
285         return snprintf(page, count, "%d\n", obd_health_check_timeout);
286 }
287
288 static int obd_proc_wr_health_timeout(struct file *file, const char *buffer,
289                                       unsigned long count, void *data)
290 {
291         int val, rc;
292
293         rc = lprocfs_write_helper(buffer, count, &val);
294         if (rc)
295                 return rc;
296
297         obd_health_check_timeout = val;
298
299         return count;
300 }
301
302 /* Root for /proc/fs/lustre */
303 struct proc_dir_entry *proc_lustre_root = NULL;
304
305 struct lprocfs_vars lprocfs_base[] = {
306         { "version", obd_proc_read_version, NULL, NULL },
307         { "kernel_version", obd_proc_read_kernel_version, NULL, NULL },
308         { "pinger", obd_proc_read_pinger, NULL, NULL },
309         { "health_check", obd_proc_read_health, NULL, NULL },
310         { "health_check_timeout", obd_proc_rd_health_timeout,
311            obd_proc_wr_health_timeout, NULL },
312         { 0 }
313 };
314 #else
315 #define lprocfs_base NULL
316 #endif /* LPROCFS */
317
318 #ifdef __KERNEL__
319 static void *obd_device_list_seq_start(struct seq_file *p, loff_t*pos)
320 {
321         if (*pos >= MAX_OBD_DEVICES)
322                 return NULL;
323         return &obd_dev[*pos];
324 }
325
326 static void obd_device_list_seq_stop(struct seq_file *p, void *v)
327 {
328 }
329
330 static void *obd_device_list_seq_next(struct seq_file *p, void *v, loff_t *pos)
331 {
332         ++*pos;
333         if (*pos >= MAX_OBD_DEVICES)
334                 return NULL;
335         return &obd_dev[*pos];
336 }
337
338 static int obd_device_list_seq_show(struct seq_file *p, void *v)
339 {
340         struct obd_device *obd = (struct obd_device *)v;
341         int index = obd - &obd_dev[0];
342         char *status;
343
344         if (!obd->obd_type)
345                 return 0;
346         if (obd->obd_stopping)
347                 status = "ST";
348         else if (obd->obd_set_up)
349                 status = "UP";
350         else if (obd->obd_attached)
351                 status = "AT";
352         else
353                 status = "--";
354
355         return seq_printf(p, "%3d %s %s %s %s %d\n",
356                           (int)index, status, obd->obd_type->typ_name,
357                           obd->obd_name, obd->obd_uuid.uuid,
358                           atomic_read(&obd->obd_refcount));
359 }
360
361 struct seq_operations obd_device_list_sops = {
362         .start = obd_device_list_seq_start,
363         .stop = obd_device_list_seq_stop,
364         .next = obd_device_list_seq_next,
365         .show = obd_device_list_seq_show,
366 };
367
368 static int obd_device_list_open(struct inode *inode, struct file *file)
369 {
370         struct proc_dir_entry *dp = PDE(inode);
371         struct seq_file *seq;
372         int rc = seq_open(file, &obd_device_list_sops);
373
374         if (rc)
375                 return rc;
376
377         seq = file->private_data;
378         seq->private = dp->data;
379
380         return 0;
381 }
382
383 struct file_operations obd_device_list_fops = {
384         .owner   = THIS_MODULE,
385         .open    = obd_device_list_open,
386         .read    = seq_read,
387         .llseek  = seq_lseek,
388         .release = seq_release,
389 };
390 #endif
391
392 int class_procfs_init(void)
393 {
394 #ifdef __KERNEL__
395         struct proc_dir_entry *entry;
396         ENTRY;
397
398         obd_sysctl_init();
399         proc_lustre_root = proc_mkdir("lustre", proc_root_fs);
400         if (!proc_lustre_root) {
401                 printk(KERN_ERR
402                        "LustreError: error registering /proc/fs/lustre\n");
403                 RETURN(-ENOMEM);
404         }
405         proc_version = lprocfs_add_vars(proc_lustre_root, lprocfs_base, NULL);
406         entry = create_proc_entry("devices", 0444, proc_lustre_root);
407         if (entry == NULL) {
408                 CERROR("error registering /proc/fs/lustre/devices\n");
409                 lprocfs_remove(proc_lustre_root);
410                 RETURN(-ENOMEM);
411         }
412         entry->proc_fops = &obd_device_list_fops;
413 #else
414         ENTRY;
415 #endif
416         RETURN(0);
417 }
418
419 #ifdef __KERNEL__
420 int class_procfs_clean(void)
421 {
422         ENTRY;
423         if (proc_lustre_root) {
424                 lprocfs_remove(proc_lustre_root);
425                 proc_lustre_root = NULL;
426         }
427         RETURN(0);
428 }
429
430
431 /* Check that we're building against the appropriate version of the Lustre
432  * kernel patch */
433 #include <linux/lustre_version.h>
434 #define LUSTRE_MIN_VERSION 37
435 #define LUSTRE_MAX_VERSION 47
436 #if (LUSTRE_KERNEL_VERSION < LUSTRE_MIN_VERSION)
437 # error Cannot continue: Your Lustre kernel patch is older than the sources
438 #elif (LUSTRE_KERNEL_VERSION > LUSTRE_MAX_VERSION)
439 # error Cannot continue: Your Lustre sources are older than the kernel patch
440 #endif
441 #endif