diff options
author | Doug Ambrisko <ambrisko@FreeBSD.org> | 2006-05-05 16:10:45 +0000 |
---|---|---|
committer | Doug Ambrisko <ambrisko@FreeBSD.org> | 2006-05-05 16:10:45 +0000 |
commit | 060e48824720626ae71cbff2328ec91f4aab1839 (patch) | |
tree | c811b5cf32301e6302a0ae1c83535f7a7e3ba038 | |
parent | f28aa7244842452f52073676ff431b6b1e87b1b7 (diff) |
Enhance the Linux emulation layer to make MegaRAID SAS managements tool happy.
Add back in a scheme to emulate old type major/minor numbers via hooks into
stat, linprocfs to return major/minors that Linux app's expect. Currently
only /dev/null is always registered. Drivers can register via the Linux
type shim similar to the ioctl shim but by using
linux_device_register_handler/linux_device_unregister_handler functions.
The structure is:
struct linux_device_handler {
char *bsd_driver_name;
char *linux_driver_name;
char *bsd_device_name;
char *linux_device_name;
int linux_major;
int linux_minor;
int linux_char_device;
};
Linprocfs uses this to display the major number of the driver. The
soon to be available linsysfs will use it to fill in the driver name.
Linux_stat uses it to translate the major/minor into Linux type values.
Note major numbers are dynamically assigned via passing in a -1 for
the major number so we don't need to keep track of them.
This is somewhat needed due to us switching to our devfs. MegaCli
will not run until I add in the linsysfs and mfi Linux compat changes.
Sponsored by: IronPort Systems
Notes
Notes:
svn path=/head/; revision=158311
-rw-r--r-- | sys/alpha/linux/linux_sysvec.c | 6 | ||||
-rw-r--r-- | sys/compat/linprocfs/linprocfs.c | 48 | ||||
-rw-r--r-- | sys/compat/linux/linux_stats.c | 64 | ||||
-rw-r--r-- | sys/compat/linux/linux_util.c | 137 | ||||
-rw-r--r-- | sys/compat/linux/linux_util.h | 17 | ||||
-rw-r--r-- | sys/i386/linux/linux_sysvec.c | 6 | ||||
-rw-r--r-- | sys/modules/linux/Makefile | 2 |
7 files changed, 263 insertions, 17 deletions
diff --git a/sys/alpha/linux/linux_sysvec.c b/sys/alpha/linux/linux_sysvec.c index 334e252b5773..b52f8286d97f 100644 --- a/sys/alpha/linux/linux_sysvec.c +++ b/sys/alpha/linux/linux_sysvec.c @@ -69,6 +69,7 @@ MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); #endif SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); +SET_DECLARE(linux_device_handler_set, struct linux_device_handler); void osendsig(sig_t catcher, ksiginfo_t *kp, sigset_t *mask); @@ -227,6 +228,7 @@ linux_elf_modevent(module_t mod, int type, void *data) Elf64_Brandinfo **brandinfo; int error; struct linux_ioctl_handler **lihp; + struct linux_device_handler **ldhp; error = 0; @@ -239,6 +241,8 @@ linux_elf_modevent(module_t mod, int type, void *data) if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_register_handler(*lihp); + SET_FOREACH(ldhp, linux_device_handler_set) + linux_device_register_handler(*ldhp); if (bootverbose) printf("Linux ELF exec handler installed\n"); } else @@ -258,6 +262,8 @@ linux_elf_modevent(module_t mod, int type, void *data) if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_unregister_handler(*lihp); + SET_FOREACH(ldhp, linux_device_handler_set) + linux_device_unregister_handler(*ldhp); if (bootverbose) printf("Linux ELF exec handler removed\n"); } else diff --git a/sys/compat/linprocfs/linprocfs.c b/sys/compat/linprocfs/linprocfs.c index b1d0f8ab7cd0..049f36c22e02 100644 --- a/sys/compat/linprocfs/linprocfs.c +++ b/sys/compat/linprocfs/linprocfs.c @@ -388,8 +388,13 @@ linprocfs_domtab(PFS_FILL_ARGS) else if (strcmp(fstype, "procfs") == 0) continue; - sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype, - mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); + if (strcmp(fstype, "linsysfs") == 0) { + sbuf_printf(sb, "/sys %s sysfs %s", mntto, + mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); + } else { + sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype, + mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); + } #define ADD_OPTION(opt, name) \ if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name); ADD_OPTION(MNT_SYNCHRONOUS, "sync"); @@ -952,7 +957,24 @@ linprocfs_donetdev(PFS_FILL_ARGS) return (0); } -#if 0 +/* + * Filler function for proc/scsi/device_info + */ +static int +linprocfs_doscsidevinfo(PFS_FILL_ARGS) +{ + return (0); +} + +/* + * Filler function for proc/scsi/scsi + */ +static int +linprocfs_doscsiscsi(PFS_FILL_ARGS) +{ + return (0); +} + extern struct cdevsw *cdevsw[]; /* @@ -961,19 +983,17 @@ extern struct cdevsw *cdevsw[]; static int linprocfs_dodevices(PFS_FILL_ARGS) { - int i; - + char *char_devices; sbuf_printf(sb, "Character devices:\n"); - for (i = 0; i < NUMCDEVSW; i++) - if (cdevsw[i] != NULL) - sbuf_printf(sb, "%3d %s\n", i, cdevsw[i]->d_name); + char_devices = linux_get_char_devices(); + sbuf_printf(sb, "%s", char_devices); + linux_free_get_char_devices(char_devices); sbuf_printf(sb, "\nBlock devices:\n"); return (0); } -#endif /* * Filler function for proc/cmdline @@ -1019,10 +1039,8 @@ linprocfs_init(PFS_INIT_ARGS) NULL, NULL, PFS_RD); pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo, NULL, NULL, PFS_RD); -#if 0 pfs_create_file(root, "devices", &linprocfs_dodevices, NULL, NULL, PFS_RD); -#endif pfs_create_file(root, "loadavg", &linprocfs_doloadavg, NULL, NULL, PFS_RD); pfs_create_file(root, "meminfo", &linprocfs_domeminfo, @@ -1031,6 +1049,8 @@ linprocfs_init(PFS_INIT_ARGS) pfs_create_file(root, "modules", &linprocfs_domodules, NULL, NULL, PFS_RD); #endif + pfs_create_file(root, "mounts", &linprocfs_domtab, + NULL, NULL, PFS_RD); pfs_create_file(root, "mtab", &linprocfs_domtab, NULL, NULL, PFS_RD); pfs_create_link(root, "self", &procfs_docurproc, @@ -1070,6 +1090,12 @@ linprocfs_init(PFS_INIT_ARGS) pfs_create_file(dir, "status", &linprocfs_doprocstatus, NULL, NULL, PFS_RD); + /* /proc/scsi/... */ + dir = pfs_create_dir(root, "scsi", NULL, NULL, 0); + pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo, + NULL, NULL, PFS_RD); + pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi, + NULL, NULL, PFS_RD); return (0); } diff --git a/sys/compat/linux/linux_stats.c b/sys/compat/linux/linux_stats.c index fa781c39cc09..7d546c482c92 100644 --- a/sys/compat/linux/linux_stats.c +++ b/sys/compat/linux/linux_stats.c @@ -46,6 +46,8 @@ __FBSDID("$FreeBSD$"); #include <sys/syscallsubr.h> #include <sys/systm.h> #include <sys/vnode.h> +#include <sys/conf.h> +#include <sys/fcntl.h> #ifdef COMPAT_LINUX32 #include <machine/../linux32/linux.h> @@ -93,6 +95,47 @@ disk_foo(struct somestat *tbuf) } #endif +static void +translate_fd_major_minor(struct thread *td, int fd, struct stat *buf) +{ + struct file *fp; + int error; + int major, minor; + + if ((error = fget(td, fd, &fp)) != 0) + return; + if (fp->f_vnode) { + if (fp->f_vnode->v_type == VCHR + || fp->f_vnode->v_type == VBLK) { + if (fp->f_vnode->v_un.vu_cdev) { + if (linux_driver_get_major_minor( + fp->f_vnode->v_un.vu_cdev->si_name, + &major, &minor) == 0) { + buf->st_rdev = (major << 8 | minor); + } + } + } + } + fdrop(fp, td); +} + +static void +translate_path_major_minor(struct thread *td, char *path, struct stat *buf) +{ + struct file *fp; + int fd; + int temp; + + temp = td->td_retval[0]; + if (kern_open(td, path, UIO_SYSSPACE, O_RDONLY, 0) != 0) + return; + fd = td->td_retval[0]; + td->td_retval[0] = temp; + translate_fd_major_minor(td, fd, buf); + fget(td, fd, &fp); + closef(fp, td); +} + static int newstat_copyout(struct stat *buf, void *ubuf) { @@ -134,13 +177,15 @@ linux_newstat(struct thread *td, struct linux_newstat_args *args) if (!error && strlen(path) > strlen("/dev/pts/") && !strncmp(path, "/dev/pts/", strlen("/dev/pts/")) && path[9] >= '0' && path[9] <= '9') { - /* - * Linux checks major and minors of the slave device to make + /* + * Linux checks major and minors of the slave device to make * sure it's a pty device, so let's make him believe it is. */ buf.st_rdev = (136 << 8); } - + + translate_path_major_minor(td, path, &buf); + LFREEPATH(path); if (error) return (error); @@ -162,6 +207,7 @@ linux_newlstat(struct thread *td, struct linux_newlstat_args *args) #endif error = kern_lstat(td, path, UIO_SYSSPACE, &sb); + translate_path_major_minor(td, path, &sb); LFREEPATH(path); if (error) return (error); @@ -180,6 +226,7 @@ linux_newfstat(struct thread *td, struct linux_newfstat_args *args) #endif error = kern_fstat(td, args->fd, &buf); + translate_fd_major_minor(td, args->fd, &buf); if (!error) error = newstat_copyout(&buf, args->buf); @@ -227,6 +274,7 @@ linux_stat(struct thread *td, struct linux_stat_args *args) error = kern_stat(td, args->path, UIO_SYSSPACE, &buf); if (error) return (error); + translate_path_major_minor(td, args->path, &buf); return(stat_copyout(&buf, args->up)); } @@ -243,6 +291,7 @@ linux_lstat(struct thread *td, struct linux_lstat_args *args) error = kern_lstat(td, args->path, UIO_SYSSPACE, &buf); if (error) return (error); + translate_path_major_minor(td, args->path, &buf); return(stat_copyout(&buf, args->up)); } #endif @@ -424,13 +473,15 @@ linux_stat64(struct thread *td, struct linux_stat64_args *args) if (!error && strlen(filename) > strlen("/dev/pts/") && !strncmp(filename, "/dev/pts/", strlen("/dev/pts/")) && filename[9] >= '0' && filename[9] <= '9') { - /* + /* * Linux checks major and minors of the slave device to make * sure it's a pty deivce, so let's make him believe it is. */ buf.st_rdev = (136 << 8); } - + + translate_path_major_minor(td, filename, &buf); + LFREEPATH(filename); if (error) return (error); @@ -452,6 +503,7 @@ linux_lstat64(struct thread *td, struct linux_lstat64_args *args) #endif error = kern_lstat(td, filename, UIO_SYSSPACE, &sb); + translate_path_major_minor(td, filename, &sb); LFREEPATH(filename); if (error) return (error); @@ -470,7 +522,9 @@ linux_fstat64(struct thread *td, struct linux_fstat64_args *args) #endif error = kern_fstat(td, args->fd, &buf); + translate_fd_major_minor(td, args->fd, &buf); if (!error) + translate_fd_major_minor(td, args->fd, &buf); error = stat64_copyout(&buf, args->statbuf); return (error); diff --git a/sys/compat/linux/linux_util.c b/sys/compat/linux/linux_util.c index b0a639167329..09c51311dc7c 100644 --- a/sys/compat/linux/linux_util.c +++ b/sys/compat/linux/linux_util.c @@ -33,8 +33,10 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/bus.h> #include <sys/lock.h> #include <sys/malloc.h> +#include <sys/linker_set.h> #include <sys/mutex.h> #include <sys/namei.h> #include <sys/proc.h> @@ -82,3 +84,138 @@ linux_msg(const struct thread *td, const char *fmt, ...) va_end(ap); printf("\n"); } + +MALLOC_DECLARE(M_LINUX); + +struct device_element +{ + TAILQ_ENTRY(device_element) list; + struct linux_device_handler entry; +}; + +static TAILQ_HEAD(, device_element) devices = + TAILQ_HEAD_INITIALIZER(devices); + +static struct linux_device_handler null_handler = + { "mem", "mem", "null", "null", 1, 3, 1}; + +DATA_SET(linux_device_handler_set, null_handler); + +char * +linux_driver_get_name_dev(device_t dev) +{ + struct device_element *de; + const char *device_name = device_get_name(dev); + + if (device_name == NULL) + return NULL; + TAILQ_FOREACH(de, &devices, list) { + if (strcmp(device_name, de->entry.bsd_driver_name) == 0) + return (de->entry.linux_driver_name); + } + + return NULL; +} + +int +linux_driver_get_major_minor(char *node, int *major, int *minor) +{ + struct device_element *de; + + if (node == NULL || major == NULL || minor == NULL) + return 1; + TAILQ_FOREACH(de, &devices, list) { + if (strcmp(node, de->entry.bsd_device_name) == 0) { + *major = de->entry.linux_major; + *minor = de->entry.linux_minor; + return 0; + } + } + + return 1; +} + +char * +linux_get_char_devices() +{ + struct device_element *de; + char *temp, *string, *last; + char formated[256]; + int current_size = 0, string_size = 1024; + + MALLOC(string, char *, string_size, M_LINUX, M_WAITOK); + string[0] = '\000'; + last = ""; + TAILQ_FOREACH(de, &devices, list) { + if (!de->entry.linux_char_device) + continue; + temp = string; + if (strcmp(last, de->entry.bsd_driver_name) != 0) { + last = de->entry.bsd_driver_name; + + snprintf(formated, sizeof(formated), "%3d %s\n", + de->entry.linux_major, + de->entry.linux_device_name); + if (strlen(formated) + current_size + >= string_size) { + string_size *= 2; + MALLOC(string, char *, string_size, + M_LINUX, M_WAITOK); + bcopy(temp, string, current_size); + FREE(temp, M_LINUX); + } + strcat(string, formated); + current_size = strlen(string); + } + } + + return string; +} + +void +linux_free_get_char_devices(char *string) +{ + FREE(string, M_LINUX); +} + +static int linux_major_starting = 200; + +int +linux_device_register_handler(struct linux_device_handler *d) +{ + struct device_element *de; + + if (d == NULL) + return (EINVAL); + + MALLOC(de, struct device_element *, sizeof(*de), + M_LINUX, M_WAITOK); + if (d->linux_major < 0) { + d->linux_major = linux_major_starting++; + } + bcopy(d, &de->entry, sizeof(*d)); + + /* Add the element to the list, sorted on span. */ + TAILQ_INSERT_TAIL(&devices, de, list); + + return (0); +} + +int +linux_device_unregister_handler(struct linux_device_handler *d) +{ + struct device_element *de; + + if (d == NULL) + return (EINVAL); + + TAILQ_FOREACH(de, &devices, list) { + if (bcmp(d, &de->entry, sizeof(*d)) == 0) { + TAILQ_REMOVE(&devices, de, list); + FREE(de, M_LINUX); + return (0); + } + } + + return (EINVAL); +} diff --git a/sys/compat/linux/linux_util.h b/sys/compat/linux/linux_util.h index 49a535d621be..b2711f8de48b 100644 --- a/sys/compat/linux/linux_util.h +++ b/sys/compat/linux/linux_util.h @@ -110,4 +110,21 @@ struct __hack void linux_msg(const struct thread *td, const char *fmt, ...) __printflike(2, 3); +struct linux_device_handler { + char *bsd_driver_name; + char *linux_driver_name; + char *bsd_device_name; + char *linux_device_name; + int linux_major; + int linux_minor; + int linux_char_device; +}; + +int linux_device_register_handler(struct linux_device_handler *h); +int linux_device_unregister_handler(struct linux_device_handler *h); +char *linux_driver_get_name_dev(device_t dev); +int linux_driver_get_major_minor(char *node, int *major, int *minor); +char *linux_get_char_devices(void); +void linux_free_get_char_devices(char *string); + #endif /* !_LINUX_UTIL_H_ */ diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index 0333acbf85ca..40e6e4875527 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -93,6 +93,7 @@ extern int linux_szsigcode; extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL]; SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); +SET_DECLARE(linux_device_handler_set, struct linux_device_handler); static int linux_fixup(register_t **stack_base, struct image_params *iparams); @@ -887,6 +888,7 @@ linux_elf_modevent(module_t mod, int type, void *data) Elf32_Brandinfo **brandinfo; int error; struct linux_ioctl_handler **lihp; + struct linux_device_handler **ldhp; error = 0; @@ -899,6 +901,8 @@ linux_elf_modevent(module_t mod, int type, void *data) if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_register_handler(*lihp); + SET_FOREACH(ldhp, linux_device_handler_set) + linux_device_register_handler(*ldhp); if (bootverbose) printf("Linux ELF exec handler installed\n"); } else @@ -918,6 +922,8 @@ linux_elf_modevent(module_t mod, int type, void *data) if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_unregister_handler(*lihp); + SET_FOREACH(ldhp, linux_device_handler_set) + linux_device_unregister_handler(*ldhp); if (bootverbose) printf("Linux ELF exec handler removed\n"); } else diff --git a/sys/modules/linux/Makefile b/sys/modules/linux/Makefile index 18f538165e52..d58e3d6f2224 100644 --- a/sys/modules/linux/Makefile +++ b/sys/modules/linux/Makefile @@ -7,7 +7,7 @@ SRCS= linux_dummy.c linux_file.c linux_getcwd.c linux_ioctl.c linux_ipc.c \ linux_machdep.c linux_mib.c linux_misc.c linux_signal.c linux_socket.c \ linux_stats.c linux_sysctl.c linux_sysent.c linux_sysvec.c \ linux_util.c opt_inet6.h opt_mac.h \ - vnode_if.h + vnode_if.h device_if.h bus_if.h OBJS= linux_locore.o .if ${MACHINE_ARCH} == "i386" |