From owner-svn-src-projects@FreeBSD.ORG Fri Aug 6 02:17:32 2010 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id CDCEC1065672; Fri, 6 Aug 2010 02:17:32 +0000 (UTC) (envelope-from jeff@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id AEBF88FC08; Fri, 6 Aug 2010 02:17:32 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o762HWHH075435; Fri, 6 Aug 2010 02:17:32 GMT (envelope-from jeff@svn.freebsd.org) Received: (from jeff@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o762HWMF075428; Fri, 6 Aug 2010 02:17:32 GMT (envelope-from jeff@svn.freebsd.org) Message-Id: <201008060217.o762HWMF075428@svn.freebsd.org> From: Jeff Roberson Date: Fri, 6 Aug 2010 02:17:32 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r210907 - projects/ofed/head/sys/ofed/include/linux X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 06 Aug 2010 02:17:32 -0000 Author: jeff Date: Fri Aug 6 02:17:32 2010 New Revision: 210907 URL: http://svn.freebsd.org/changeset/base/210907 Log: - Add Linux kobj objects to the sysctl namespace in sys to mirror the /sysfs filesystem. All linux objects are treated as strings with a custom proc that calls the show and store interface methods. - Add all device attributes and class attributes. - Complete adding ktypes for classes and devices so they are now properly reference counted. Sponsored by: Isilon Systems, iX Systems, and Panasas. Modified: projects/ofed/head/sys/ofed/include/linux/device.h projects/ofed/head/sys/ofed/include/linux/kobject.h projects/ofed/head/sys/ofed/include/linux/linux_compat.c projects/ofed/head/sys/ofed/include/linux/miscdevice.h projects/ofed/head/sys/ofed/include/linux/pci.h projects/ofed/head/sys/ofed/include/linux/sysfs.h Modified: projects/ofed/head/sys/ofed/include/linux/device.h ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/device.h Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/device.h Fri Aug 6 02:17:32 2010 (r210907) @@ -47,7 +47,10 @@ typedef enum irqreturn irqreturn_t; struct class { const char *name; struct module *owner; + struct kobject kobj; devclass_t bsdclass; + void (*class_release)(struct class *class); + void (*dev_release)(struct device *dev); }; struct device { @@ -65,12 +68,13 @@ struct device { }; -/* #define device linux_device */ +extern struct device linux_rootdev; +extern struct kobject class_root; struct class_attribute { struct attribute attr; ssize_t (*show)(struct class *, char *); - ssize_t (*store)(struct class *, char *, size_t); + ssize_t (*store)(struct class *, const char *, size_t); }; #define CLASS_ATTR(_name, _mode, _show, _store) \ struct class_attribute class_attr_##_name = \ @@ -81,7 +85,8 @@ struct device_attribute { ssize_t (*show)(struct device *, struct device_attribute *, char *); ssize_t (*store)(struct device *, - struct device_attribute *, const char *, size_t); + struct device_attribute *, const char *, + size_t); }; #define DEVICE_ATTR(_name, _mode, _show, _store) \ @@ -126,7 +131,7 @@ dev_name(const struct device *dev) } #define dev_set_name(_dev, _fmt, ...) \ - kobject_set_name(&(_dev)->kobj, (_fmt), #__VA_ARGS__) + kobject_set_name(&(_dev)->kobj, (_fmt), ##__VA_ARGS__) static inline void put_device(struct device *dev) @@ -136,21 +141,121 @@ put_device(struct device *dev) kobject_put(&dev->kobj); } +static inline ssize_t +class_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct class_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct class_attribute, attr); + error = -EIO; + if (dattr->show) + error = dattr->show(container_of(kobj, struct class, kobj), + buf); + return (error); +} + +static inline ssize_t +class_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct class_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct class_attribute, attr); + error = -EIO; + if (dattr->store) + error = dattr->store(container_of(kobj, struct class, kobj), + buf, count); + return (error); +} + +static inline void +class_release(struct kobject *kobj) +{ + struct class *class; + + class = container_of(kobj, struct class, kobj); + if (class->class_release) + class->class_release(class); +} + +static struct sysfs_ops class_sysfs = { + .show = class_show, + .store = class_store, +}; +static struct kobj_type class_ktype = { + .release = class_release, + .sysfs_ops = &class_sysfs +}; + static inline int class_register(struct class *class) { class->bsdclass = devclass_create(class->name); - return 0; + kobject_init(&class->kobj, &class_ktype); + kobject_set_name(&class->kobj, class->name); + kobject_add(&class->kobj, &class_root, class->name); + + return (0); } static inline void class_unregister(struct class *class) { - return; + kobject_put(&class->kobj); } +static inline void +device_release(struct kobject *kobj) +{ + struct device *dev; + + dev = container_of(kobj, struct device, kobj); + /* This is the precedence defined by linux. */ + if (dev->release) + dev->release(dev); + else if (dev->class && dev->class->dev_release) + dev->class->dev_release(dev); +} + +static inline ssize_t +dev_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct device_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct device_attribute, attr); + error = -EIO; + if (dattr->show) + error = dattr->show(container_of(kobj, struct device, kobj), + dattr, buf); + return (error); +} + +static inline ssize_t +dev_store(struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + struct device_attribute *dattr; + ssize_t error; + + dattr = container_of(attr, struct device_attribute, attr); + error = -EIO; + if (dattr->store) + error = dattr->store(container_of(kobj, struct device, kobj), + dattr, buf, count); + return (error); +} + +static struct sysfs_ops dev_sysfs = { .show = dev_show, .store = dev_store, }; +static struct kobj_type dev_ktype = { + .release = device_release, + .sysfs_ops = &dev_sysfs +}; + /* * Devices are registered and created for exporting to sysfs. create * implies register and register assumes the device fields have been @@ -177,7 +282,8 @@ device_register(struct device *dev) device_set_softc(bsddev, dev); } dev->bsddev = bsddev; - kobject_init(&dev->kobj, NULL); + kobject_init(&dev->kobj, &dev_ktype); + kobject_add(&dev->kobj, &dev->class->kobj, dev_name(dev)); get_device(dev); return (0); @@ -215,14 +321,28 @@ device_destroy(struct class *class, dev_ } } +static inline void +class_kfree(struct class *class) +{ + + kfree(class); +} + static inline struct class * class_create(struct module *owner, const char *name) { struct class *class; + int error; class = kzalloc(sizeof(*class), M_WAITOK); class->owner = owner; class->name= name; + class->class_release = class_kfree; + error = class_register(class); + if (error) { + kfree(class); + return (NULL); + } return (class); } @@ -230,36 +350,44 @@ class_create(struct module *owner, const static inline void class_destroy(struct class *class) { - /* XXX Missing ref count. */ - kfree(class); + + if (class == NULL) + return; + class_unregister(class); } -/* - * These are supposed to create the sysfs entry for the attribute. Should - * instead create a sysctl tree. XXX - */ static inline int -device_create_file(struct device *device, const struct device_attribute *entry) +device_create_file(struct device *dev, const struct device_attribute *attr) { - return (0); + + if (dev) + return sysfs_create_file(&dev->kobj, &attr->attr); + return -EINVAL; } static inline void device_remove_file(struct device *dev, const struct device_attribute *attr) { - return; + + if (dev) + sysfs_remove_file(&dev->kobj, &attr->attr); } static inline int class_create_file(struct class *class, const struct class_attribute *attr) { - return (0); + + if (class) + return sysfs_create_file(&class->kobj, &attr->attr); + return -EINVAL; } static inline void class_remove_file(struct class *class, const struct class_attribute *attr) { - return; + + if (class) + sysfs_remove_file(&class->kobj, &attr->attr); } #endif /* _LINUX_DEVICE_H_ */ Modified: projects/ofed/head/sys/ofed/include/linux/kobject.h ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/kobject.h Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/kobject.h Fri Aug 6 02:17:32 2010 (r210907) @@ -35,6 +35,7 @@ #include struct kobject; +struct sysctl_oid; struct kobj_type { void (*release)(struct kobject *kobj); @@ -42,11 +43,15 @@ struct kobj_type { struct attribute **default_attrs; }; +extern struct kobj_type kfree_type; + struct kobject { struct kobject *parent; char *name; struct kref kref; struct kobj_type *ktype; + struct list_head entry; + struct sysctl_oid *oidp; }; static inline void @@ -54,20 +59,13 @@ kobject_init(struct kobject *kobj, struc { kref_init(&kobj->kref); + INIT_LIST_HEAD(&kobj->entry); kobj->ktype = ktype; - kobj->name = NULL; - kobj->parent = NULL; + kobj->oidp = NULL; } -static inline void -kobject_release(struct kref *kref) -{ - struct kobject *kobj; - - kobj = container_of(kref, struct kobject, kref); - if (kobj->ktype && kobj->ktype->release) - kobj->ktype->release(kobj); -} +static inline void kobject_put(struct kobject *kobj); +void kobject_release(struct kref *kref); static inline void kobject_put(struct kobject *kobj) @@ -109,24 +107,42 @@ kobject_set_name_vargs(struct kobject *k return (0); } -static inline int -kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) +int kobject_add(struct kobject *kobj, struct kobject *parent, + const char *fmt, ...); + +static inline struct kobject * +kobject_create(void) { - va_list args; - int error; + struct kobject *kobj; - va_start(args, fmt); - error = kobject_set_name_vargs(kobj, fmt, args); - va_end(args); - kobj->parent = parent; + kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); + if (kobj == NULL) + return (NULL); + kobject_init(kobj, &kfree_type); - return (error); + return (kobj); +} + +static inline struct kobject * +kobject_create_and_add(const char *name, struct kobject *parent) +{ + struct kobject *kobj; + + kobj = kobject_create(); + if (kobj == NULL) + return (NULL); + if (kobject_add(kobj, parent, "%s", name) == 0) + return (kobj); + kobject_put(kobj); + + return (NULL); } static inline char * kobject_name(const struct kobject *kobj) { + return kobj->name; } Modified: projects/ofed/head/sys/ofed/include/linux/linux_compat.c ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/linux_compat.c Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/linux_compat.c Fri Aug 6 02:17:32 2010 (r210907) @@ -41,17 +41,23 @@ #include #include #include +#include +#include +#include MALLOC_DEFINE(M_KMALLOC, "linux", "Linux kmalloc compat"); -MALLOC_DEFINE(M_LINUX_DMA, "lnxdma", "Linux DMA compat"); + +struct fileops linuxfileops; +struct cdevsw linuxcdevsw; #include /* Undo Linux compat change. */ #undef RB_ROOT #define RB_ROOT(head) (head)->rbh_root +struct kobject class_root; +struct device linux_rootdev; struct class miscclass; -struct device miscroot; struct list_head pci_drivers; int @@ -75,6 +81,71 @@ kobject_set_name(struct kobject *kobj, c return (error); } +static inline int +kobject_add_complete(struct kobject *kobj, struct kobject *parent) +{ + struct kobj_type *t; + int error; + + kobj->parent = kobject_get(parent); + error = sysfs_create_dir(kobj); + if (error == 0 && kobj->ktype && kobj->ktype->default_attrs) { + struct attribute **attr; + t = kobj->ktype; + + for (attr = t->default_attrs; *attr != NULL; attr++) { + error = sysfs_create_file(kobj, *attr); + if (error) + break; + } + if (error) + sysfs_remove_dir(kobj); + + } + return (error); +} + +int +kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) +{ + va_list args; + int error; + + va_start(args, fmt); + error = kobject_set_name_vargs(kobj, fmt, args); + va_end(args); + if (error) + return (error); + + return kobject_add_complete(kobj, parent); +} + +void +kobject_release(struct kref *kref) +{ + struct kobject *kobj; + char *name; + + kobj = container_of(kref, struct kobject, kref); + sysfs_remove_dir(kobj); + if (kobj->parent) + kobject_put(kobj->parent); + kobj->parent = NULL; + name = kobj->name; + if (kobj->ktype && kobj->ktype->release) + kobj->ktype->release(kobj); + kfree(name); +} + +static void +kobject_kfree(struct kobject *kobj) +{ + + kfree(kobj); +} + +struct kobj_type kfree_type = { .release = kobject_kfree }; + struct device * device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) @@ -84,6 +155,7 @@ device_create(struct class *class, struc dev = kzalloc(sizeof(*dev), M_WAITOK); dev->parent = parent; + dev->class = class; dev->devt = devt; dev->driver_data = drvdata; va_start(args, fmt); @@ -109,17 +181,31 @@ kobject_init_and_add(struct kobject *kob va_start(args, fmt); error = kobject_set_name_vargs(kobj, fmt, args); va_end(args); - - return error; + if (error) + return (error); + return kobject_add_complete(kobj, parent); } static void linux_compat_init(void) { + struct sysctl_oid *rootoid; + + rootoid = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(), + OID_AUTO, "sys", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "sys"); + kobject_init(&class_root, &class_ktype); + kobject_set_name(&class_root, "class"); + class_root.oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(rootoid), + OID_AUTO, "class", CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, "class"); + kobject_init(&linux_rootdev.kobj, &dev_ktype); + kobject_set_name(&linux_rootdev.kobj, "device"); + linux_rootdev.kobj.oidp = SYSCTL_ADD_NODE(NULL, + SYSCTL_CHILDREN(rootoid), OID_AUTO, "device", CTLFLAG_RD, NULL, + "device"); + linux_rootdev.bsddev = root_bus; miscclass.name = "misc"; class_register(&miscclass); - miscroot.bsddev = root_bus; INIT_LIST_HEAD(&pci_drivers); } -module_init(linux_compat_init); +SYSINIT(linux_compat, SI_SUB_DRIVERS, SI_ORDER_SECOND, linux_compat_init, NULL); Modified: projects/ofed/head/sys/ofed/include/linux/miscdevice.h ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/miscdevice.h Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/miscdevice.h Fri Aug 6 02:17:32 2010 (r210907) @@ -41,7 +41,6 @@ struct miscdevice { }; extern struct class miscclass; -extern struct device miscroot; /* * XXX Missing cdev. @@ -49,7 +48,7 @@ extern struct device miscroot; static inline int misc_register(struct miscdevice *misc) { - misc->this_device = device_create(&miscclass, &miscroot, 0, misc, + misc->this_device = device_create(&miscclass, &linux_rootdev, 0, misc, misc->name); return (0); } Modified: projects/ofed/head/sys/ofed/include/linux/pci.h ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/pci.h Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/pci.h Fri Aug 6 02:17:32 2010 (r210907) @@ -373,12 +373,15 @@ linux_pci_attach(device_t dev) pdrv = linux_pci_find(dev, &id); pdev = device_get_softc(dev); + pdev->dev.parent = &linux_rootdev; pdev->dev.bsddev = dev; pdev->device = device_get_unit(dev); pdev->dev.dma_mask = &pdev->dma_mask; pdev->pdrv = pdrv; - kobject_init(&pdev->dev.kobj, NULL); + kobject_init(&pdev->dev.kobj, &dev_ktype); kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); + kobject_add(&pdev->dev.kobj, &linux_rootdev.kobj, + kobject_name(&pdev->dev.kobj)); rle = _pci_get_rle(pdev, SYS_RES_IRQ, 0); if (rle) pdev->irq = rle->start; Modified: projects/ofed/head/sys/ofed/include/linux/sysfs.h ============================================================================== --- projects/ofed/head/sys/ofed/include/linux/sysfs.h Fri Aug 6 01:08:12 2010 (r210906) +++ projects/ofed/head/sys/ofed/include/linux/sysfs.h Fri Aug 6 02:17:32 2010 (r210907) @@ -29,6 +29,8 @@ #ifndef _LINUX_SYSFS_H_ #define _LINUX_SYSFS_H_ +#include + struct attribute { const char *name; struct module *owner; @@ -60,4 +62,121 @@ struct attribute_group { #define __ATTR_NULL { .attr = { .name = NULL } } +/* + * Handle our generic '\0' terminated 'C' string. + * Two cases: + * a variable string: point arg1 at it, arg2 is max length. + * a constant string: point arg1 at it, arg2 is zero. + */ + +static inline int +sysctl_handle_attr(SYSCTL_HANDLER_ARGS) +{ + struct kobject *kobj; + struct attribute *attr; + const struct sysfs_ops *ops; + void *buf; + int error; + ssize_t len; + + kobj = arg1; + attr = (struct attribute *)arg2; + buf = (void *)get_zeroed_page(GFP_KERNEL); + len = 1; /* Copy out a NULL byte at least. */ + if (kobj->ktype == NULL || kobj->ktype->sysfs_ops == NULL) + return (ENODEV); + ops = kobj->ktype->sysfs_ops; + if (buf == NULL) + return (ENOMEM); + if (ops->show) { + len = ops->show(kobj, attr, buf); + /* + * It's valid not to have a 'show' so we just return 1 byte + * of NULL. + */ + if (len < 0) { + error = -len; + len = 1; + if (error != EIO) + goto out; + } + } + error = SYSCTL_OUT(req, buf, len); + if (error || !req->newptr || ops->store == NULL) + goto out; + error = SYSCTL_IN(req, buf, PAGE_SIZE); + if (error) + goto out; + len = ops->store(kobj, attr, buf, req->newlen); + if (len < 0) + error = -len; +out: + free_page((unsigned long)buf); + + return (error); +} + +static inline int +sysfs_create_file(struct kobject *kobj, const struct attribute *attr) +{ + + sysctl_add_oid(NULL, SYSCTL_CHILDREN(kobj->oidp), OID_AUTO, + attr->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, + (uintptr_t)attr, sysctl_handle_attr, "A", ""); + + return (0); +} + +static inline void +sysfs_remove_file(struct kobject *kobj, const struct attribute *attr) +{ + + if (kobj->oidp) + sysctl_remove_name(kobj->oidp, attr->name, 1, 1); +} + +static inline void +sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) +{ + + if (kobj->oidp) + sysctl_remove_name(kobj->oidp, grp->name, 1, 1); +} + +static inline int +sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) +{ + struct attribute **attr; + struct sysctl_oid *oidp; + + oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->oidp), + OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); + for (attr = grp->attrs; *attr != NULL; attr++) { + sysctl_add_oid(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, + (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, + kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); + } + + return (0); +} + +static inline int +sysfs_create_dir(struct kobject *kobj) +{ + + kobj->oidp = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(kobj->parent->oidp), + OID_AUTO, kobj->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, kobj->name); + + return (0); +} + +static inline void +sysfs_remove_dir(struct kobject *kobj) +{ + + if (kobj->oidp == NULL) + return; + sysctl_remove_oid(kobj->oidp, 1, 1); +} + #endif /* _LINUX_SYSFS_H_ */