Commit cfc0e9b7 authored by Paul Mackerras's avatar Paul Mackerras

Merge samba.org:/home/paulus/kernel/linux-2.5

into samba.org:/home/paulus/kernel/for-linus-ppc
parents 3f379e03 ff7b0319
......@@ -170,7 +170,6 @@ static void __init geode_configure(void)
{
unsigned long flags;
u8 ccr3, ccr4;
unsigned long cr0;
local_irq_save(flags);
ccr3 = getCx86(CX86_CCR3);
......
......@@ -1749,10 +1749,11 @@ void __init setup_IO_APIC(void)
* APIC bugs then we can allow the modify fast path
*/
static void __init io_apic_bug_finalize(void)
static int __init io_apic_bug_finalize(void)
{
if(sis_apic_bug == -1)
sis_apic_bug = 0;
return 0;
}
late_initcall(io_apic_bug_finalize);
......
#undef DEBUG
#ifdef DEBUG
# define DBG(x...) printk(x)
#else
# define DBG(x...)
#endif
extern struct list_head global_device_list;
extern spinlock_t device_lock;
extern struct semaphore device_sem;
extern int bus_add_device(struct device * dev);
......@@ -22,8 +14,8 @@ extern void devclass_remove_device(struct device *);
extern int devclass_add_driver(struct device_driver *);
extern void devclass_remove_driver(struct device_driver *);
extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *);
extern int interface_add_dev(struct device *);
extern void interface_remove_dev(struct device *);
#ifdef CONFIG_HOTPLUG
......
......@@ -132,6 +132,91 @@ struct subsystem bus_subsys = {
};
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct list_head * head, * entry;
int error = 0;
if (!(bus = get_bus(bus)))
return -EINVAL;
head = start ? &start->bus_list : &bus->devices;
down_read(&bus->subsys.rwsem);
list_for_each(entry,head) {
struct device * dev = get_device(to_dev(entry));
error = fn(dev,data);
put_device(dev);
if (error)
break;
}
up_read(&bus->subsys.rwsem);
return error;
}
/**
* bus_for_each_drv - driver iterator
* @bus: bus we're dealing with.
* @start: driver to start iterating on.
* @data: data to pass to the callback.
* @fn: function to call for each driver.
*
* This is nearly identical to the device iterator above.
* We iterate over each driver that belongs to @bus, and call
* @fn for each. If @fn returns anything but 0, we break out
* and return it. If @start is not NULL, we use it as the head
* of the list.
*
* NOTE: we don't return the driver that returns a non-zero
* value, nor do we leave the reference count incremented for that
* driver. If the caller needs to know that info, it must set it
* in the callback. It must also be sure to increment the refcount
* so it doesn't disappear before returning to the caller.
*/
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *))
{
struct list_head * head, * entry;
int error = 0;
if(!(bus = get_bus(bus)))
return -EINVAL;
head = start ? &start->bus_list : &bus->drivers;
down_read(&bus->subsys.rwsem);
list_for_each(entry,head) {
struct device_driver * drv = get_driver(to_drv(entry));
error = fn(drv,data);
put_driver(drv);
if(error)
break;
}
up_read(&bus->subsys.rwsem);
return error;
}
/**
* attach - add device to driver.
* @dev: device.
......@@ -190,7 +275,7 @@ static int bus_match(struct device * dev, struct device_driver * drv)
*/
static int device_attach(struct device * dev)
{
struct bus_type * bus = dev->bus;
struct bus_type * bus = dev->bus;
struct list_head * entry;
int error = 0;
......@@ -455,6 +540,9 @@ static int __init bus_subsys_init(void)
core_initcall(bus_subsys_init);
EXPORT_SYMBOL(bus_for_each_dev);
EXPORT_SYMBOL(bus_for_each_drv);
EXPORT_SYMBOL(bus_add_device);
EXPORT_SYMBOL(bus_remove_device);
EXPORT_SYMBOL(bus_register);
......
......@@ -2,7 +2,7 @@
* class.c - basic device class management
*/
#undef DEBUG
#define DEBUG
#include <linux/device.h>
#include <linux/module.h>
......@@ -98,7 +98,6 @@ void devclass_remove_file(struct device_class * cls, struct devclass_attribute *
}
int devclass_add_driver(struct device_driver * drv)
{
struct device_class * cls = get_devclass(drv->devclass);
......@@ -172,9 +171,11 @@ int devclass_add_device(struct device * dev)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
interface_add(cls,dev);
interface_add_dev(dev);
}
list_add_tail(&dev->class_list,&cls->devices);
/* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "add");
......@@ -196,9 +197,11 @@ void devclass_remove_device(struct device * dev)
down_write(&cls->subsys.rwsem);
pr_debug("device class %s: removing device %s\n",
cls->name,dev->name);
interface_remove(cls,dev);
interface_remove_dev(dev);
unenum_device(cls,dev);
list_del(&dev->class_list);
/* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "remove");
......@@ -224,6 +227,7 @@ void put_devclass(struct device_class * cls)
int devclass_register(struct device_class * cls)
{
INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->devices);
pr_debug("device class '%s': registering\n",cls->name);
strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN);
......
......@@ -18,15 +18,11 @@
#include "base.h"
LIST_HEAD(global_device_list);
int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
DECLARE_MUTEX(device_sem);
spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
#define to_dev(obj) container_of(obj,struct device,kobj)
......@@ -146,11 +142,10 @@ void device_initialize(struct device *dev)
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list);
INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->class_list);
INIT_LIST_HEAD(&dev->intf_list);
// spin_lock_init(&dev->lock);
}
/**
......@@ -188,13 +183,11 @@ int device_add(struct device *dev)
goto register_done;
/* now take care of our own registration */
down(&device_sem);
if (parent) {
list_add_tail(&dev->g_list,&dev->parent->g_list);
down(&device_sem);
list_add_tail(&dev->node,&parent->children);
} else
list_add_tail(&dev->g_list,&global_device_list);
up(&device_sem);
up(&device_sem);
}
bus_add_device(dev);
......@@ -276,10 +269,11 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
down(&device_sem);
list_del_init(&dev->node);
list_del_init(&dev->g_list);
up(&device_sem);
if (parent) {
down(&device_sem);
list_del_init(&dev->node);
up(&device_sem);
}
/* Notify the platform of the removal, in case they
* need to do anything...
......
......@@ -2,7 +2,7 @@
* intf.c - class-specific interface management
*/
#undef DEBUG
#define DEBUG
#include <linux/device.h>
#include <linux/module.h>
......@@ -10,98 +10,267 @@
#include "base.h"
#define to_intf(node) container_of(node,struct device_interface,kobj.entry)
#define to_intf(node) container_of(node,struct device_interface,subsys.kobj.entry)
#define to_data(e) container_of(e,struct intf_data,kobj.entry)
#define intf_from_data(d) container_of(d->kobj.subsys,struct device_interface, subsys);
/**
* intf_dev_link - symlink from interface's directory to device's directory
* intf_dev_link - create sysfs symlink for interface.
* @data: interface data descriptor.
*
* Create a symlink 'phys' in the interface's directory to
*/
static int intf_dev_link(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname);
char name[16];
snprintf(name,16,"%d",data->intf_num);
return sysfs_create_link(&data->intf->subsys.kobj,&data->dev->kobj,name);
}
/**
* intf_dev_unlink - remove symlink for interface.
* @intf: interface data descriptor.
*
*/
static void intf_dev_unlink(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
sysfs_remove_link(&data->intf->kobj,linkname);
char name[16];
snprintf(name,16,"%d",data->intf_num);
sysfs_remove_link(&data->intf->subsys.kobj,name);
}
/**
* interface_add_data - attach data descriptor
* @data: interface data descriptor.
*
* This attaches the per-instance interface object to the
* interface (by registering its kobject) and the device
* itself (by inserting it into the device's list).
*
* Note that there is no explicit protection done in this
* function. This should be called from the interface's
* add_device() method, which is called under the protection
* of the class's rwsem.
*/
int interface_add_data(struct intf_data * data)
{
struct device_interface * intf = intf_from_data(data);
data->intf_num = data->intf->devnum++;
data->kobj.subsys = &intf->subsys;
kobject_register(&data->kobj);
list_add_tail(&data->dev_entry,&data->dev->intf_list);
intf_dev_link(data);
return 0;
}
/**
* interface_remove_data - detach data descriptor.
* @data: interface data descriptor.
*
* This detaches the per-instance data descriptor by removing
* it from the device's list and unregistering the kobject from
* the subsystem.
*/
void interface_remove_data(struct intf_data * data)
{
intf_dev_unlink(data);
list_del_init(&data->dev_entry);
kobject_unregister(&data->kobj);
}
/**
* add - attach device to interface
* @intf: interface.
* @dev: device.
*
* This is just a simple helper. Check the interface's interface
* helper and call it. This is called when adding an interface
* the class's devices, or a device to the class's interfaces.
*/
static int add(struct device_interface * intf, struct device * dev)
{
int error = 0;
if (intf->add_device)
error = intf->add_device(dev);
pr_debug(" -> %s (%d)\n",dev->bus_id,error);
return error;
}
/**
* del - detach device from interface.
* @data: interface data descriptor.
*
* Another simple helper. Remove the data descriptor from
* the device and the interface, then call the interface's
* remove_device() method.
*/
static void del(struct intf_data * data)
{
struct device_interface * intf = intf_from_data(data);
pr_debug(" -> %s ",data->intf->name);
interface_remove_data(data);
if (intf->remove_device)
intf->remove_device(data);
}
#define to_dev(entry) container_of(entry,struct device,class_list)
/**
* add_intf - add class's devices to interface.
* @intf: interface.
*
* Loop over the devices registered with the class, and call
* the interface's add_device() method for each.
*
* On an error, we won't break, but we will print debugging info.
*/
static void add_intf(struct device_interface * intf)
{
struct device_class * cls = intf->devclass;
struct list_head * entry;
down_write(&cls->subsys.rwsem);
list_for_each(entry,&cls->devices)
add(intf,to_dev(entry));
up_write(&cls->subsys.rwsem);
}
/**
* interface_register - register an interface with a device class.
* @intf: interface.
*
* An interface may be loaded after drivers and devices have been
* added to the class. So, we must add each device already known to
* the class to the interface as its registered.
*/
int interface_register(struct device_interface * intf)
{
struct device_class * cls = get_devclass(intf->devclass);
int error = 0;
if (cls) {
pr_debug("register interface '%s' with class '%s'\n",
intf->name,cls->name);
strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN);
intf->kobj.subsys = &cls->subsys;
kobject_register(&intf->kobj);
} else
error = -EINVAL;
return error;
strncpy(intf->subsys.kobj.name,intf->name,KOBJ_NAME_LEN);
intf->subsys.kobj.subsys = &cls->subsys;
subsystem_register(&intf->subsys);
add_intf(intf);
}
return 0;
}
/**
* del_intf - remove devices from interface.
* @intf: interface being unloaded.
*
* This loops over the devices registered with a class and
* calls the interface's remove_device() method for each.
* This is called when an interface is being unregistered.
*/
static void del_intf(struct device_interface * intf)
{
struct list_head * entry;
down_write(&intf->devclass->subsys.rwsem);
list_for_each(entry,&intf->subsys.list) {
struct intf_data * data = to_data(entry);
del(data);
}
up_write(&intf->devclass->subsys.rwsem);
}
/**
* interface_unregister - remove interface from class.
* @intf: interface.
*
* This is called when an interface in unloaded, giving it a
* chance to remove itself from devicse that have been added to
* it.
*/
void interface_unregister(struct device_interface * intf)
{
pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,intf->devclass->name);
kobject_unregister(&intf->kobj);
struct device_class * cls = intf->devclass;
if (cls) {
pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,cls->name);
del_intf(intf);
subsystem_unregister(&intf->subsys);
put_devclass(cls);
}
}
int interface_add(struct device_class * cls, struct device * dev)
/**
* interface_add_dev - add device to interfaces.
* @dev: device.
*
* This is a helper for the class driver core. When a
* device is being added to a class, this is called to add
* the device to all the interfaces in the class.
*
* The operation is simple enough: loop over the interfaces
* and call add() [above] for each. The class rwsem is assumed
* to be held.
*/
int interface_add_dev(struct device * dev)
{
struct device_class * cls = dev->driver->devclass;
struct list_head * node;
int error = 0;
pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name);
pr_debug("interfaces: adding device %s\n",dev->name);
list_for_each(node,&cls->subsys.list) {
struct device_interface * intf = to_intf(node);
if (intf->add_device) {
error = intf->add_device(dev);
if (error)
pr_debug("%s:%s: adding '%s' failed: %d\n",
cls->name,intf->name,dev->name,error);
}
add(intf,dev);
}
return 0;
}
void interface_remove(struct device_class * cls, struct device * dev)
{
struct list_head * node;
struct list_head * next;
pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name);
/**
* interface_remove_dev - remove device from interfaces.
* @dev: device.
*
* This is another helper for the class driver core, and called
* when the device is being removed from the class.
*
* We iterate over the list of interface data descriptors attached
* to the device, and call del() [above] for each. Again, the
* class's rwsem is assumed to be held during this.
*/
list_for_each_safe(node,next,&dev->intf_list) {
struct intf_data * intf_data = container_of(node,struct intf_data,node);
list_del_init(&intf_data->node);
void interface_remove_dev(struct device * dev)
{
struct list_head * entry, * next;
intf_dev_unlink(intf_data);
pr_debug("%s ",intf_data->intf->name);
if (intf_data->intf->remove_device)
intf_data->intf->remove_device(intf_data);
}
pr_debug("\n");
}
pr_debug("interfaces: removing device %s\n",dev->name);
int interface_add_data(struct intf_data * data)
{
down_write(&data->intf->devclass->subsys.rwsem);
list_add_tail(&data->node,&data->dev->intf_list);
data->intf_num = data->intf->devnum++;
intf_dev_link(data);
up_write(&data->intf->devclass->subsys.rwsem);
return 0;
list_for_each_safe(entry,next,&dev->intf_list) {
struct intf_data * intf_data = to_data(entry);
del(intf_data);
}
}
EXPORT_SYMBOL(interface_register);
......
......@@ -93,7 +93,7 @@ int __init register_node(struct node *node, int num, struct node *parent)
static int __init register_node_type(void)
{
driver_register(&node_driver);
return devclass_register(&node_devclass);
devclass_register(&node_devclass);
return driver_register(&node_driver);
}
postcore_initcall(register_node_type);
......@@ -15,7 +15,9 @@
#include <asm/semaphore.h>
#include "base.h"
#define to_dev(node) container_of(node,struct device,g_list)
#define to_dev(node) container_of(node,struct device,kobj.entry)
extern struct subsystem device_subsys;
/**
* device_suspend - suspend/remove all devices on the device ree
......@@ -35,8 +37,8 @@ int device_suspend(u32 state, u32 level)
printk(KERN_EMERG "Suspending devices\n");
down(&device_sem);
list_for_each(node,&global_device_list) {
down_write(&device_subsys.rwsem);
list_for_each(node,&device_subsys.list) {
struct device * dev = to_dev(node);
if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name);
......@@ -45,7 +47,7 @@ int device_suspend(u32 state, u32 level)
printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
}
}
up(&device_sem);
up_write(&device_subsys.rwsem);
return error;
}
......@@ -61,15 +63,15 @@ void device_resume(u32 level)
{
struct list_head * node;
down(&device_sem);
list_for_each_prev(node,&global_device_list) {
down_write(&device_subsys.rwsem);
list_for_each_prev(node,&device_subsys.list) {
struct device * dev = to_dev(node);
if (dev->driver && dev->driver->resume) {
pr_debug("resuming device %s\n",dev->name);
dev->driver->resume(dev,level);
}
}
up(&device_sem);
up_write(&device_subsys.rwsem);
printk(KERN_EMERG "Devices Resumed\n");
}
......@@ -83,15 +85,15 @@ void device_shutdown(void)
printk(KERN_EMERG "Shutting down devices\n");
down(&device_sem);
list_for_each(entry,&global_device_list) {
down_write(&device_subsys.rwsem);
list_for_each(entry,&device_subsys.list) {
struct device * dev = to_dev(entry);
if (dev->driver && dev->driver->shutdown) {
pr_debug("shutting down %s\n",dev->name);
dev->driver->shutdown(dev);
}
}
up(&device_sem);
up_write(&device_subsys.rwsem);
}
EXPORT_SYMBOL(device_suspend);
......
......@@ -3977,18 +3977,6 @@ static void __init register_devfs_entries (int drive)
}
}
static void unregister_devfs_entries (int drive)
{
int i;
if (UDP->cmos < NUMBER(default_drive_params)) {
i = 0;
do {
devfs_remove("floppy/%d%s", drive, table[table_sup[UDP->cmos][i]]);
} while (table_sup[UDP->cmos][i++]);
}
}
/*
* Floppy Driver initialization
* =============================
......@@ -4543,6 +4531,18 @@ static void floppy_release_irq_and_dma(void)
char *floppy;
static void unregister_devfs_entries (int drive)
{
int i;
if (UDP->cmos < NUMBER(default_drive_params)) {
i = 0;
do {
devfs_remove("floppy/%d%s", drive, table[table_sup[UDP->cmos][i]]);
} while (table_sup[UDP->cmos][i++]);
}
}
static void __init parse_floppy_cfg_string(char *cfg)
{
char *ptr;
......
......@@ -410,11 +410,11 @@ struct gendisk *alloc_disk(int minors)
disk->minors = minors;
while (minors >>= 1)
disk->minor_shift++;
kobject_init(&disk->kobj);
disk->kobj.subsys = &block_subsys;
kobject_init(&disk->kobj);
INIT_LIST_HEAD(&disk->full_list);
rand_initialize_disk(disk);
}
rand_initialize_disk(disk);
return disk;
}
......
......@@ -448,6 +448,7 @@ struct pci_dev * __devinit pci_scan_device(struct pci_dev *temp)
/* now put in global tree */
strcpy(dev->dev.bus_id,dev->slot_name);
dev->dev.dma_mask = &dev->dma_mask;
device_register(&dev->dev);
return dev;
......
......@@ -80,6 +80,7 @@
/* host controllers we manage */
LIST_HEAD (usb_bus_list);
EXPORT_SYMBOL (usb_bus_list);
/* used when allocating bus numbers */
#define USB_MAXBUS 64
......@@ -90,6 +91,7 @@ static struct usb_busmap busmap;
/* used when updating list of hcds */
DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
EXPORT_SYMBOL (usb_bus_list_lock);
/* used when updating hcd data */
static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
......
......@@ -23,6 +23,7 @@
#include <linux/pagevec.h>
#include <linux/quotaops.h>
#include <linux/dnotify.h>
#include <linux/security.h>
#include <asm/uaccess.h>
......
......@@ -377,7 +377,6 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
p->start_sect = start;
p->nr_sects = len;
devfs_register_partition(disk, part);
kobject_init(&p->kobj);
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part);
p->kobj.parent = &disk->kobj;
p->kobj.subsys = &part_subsys;
......@@ -406,7 +405,7 @@ void register_disk(struct gendisk *disk)
s = strchr(disk->kobj.name, '/');
if (s)
*s = '!';
kobject_register(&disk->kobj);
kobject_add(&disk->kobj);
disk_sysfs_symlinks(disk);
if (disk->flags & GENHD_FL_CD)
......@@ -529,8 +528,7 @@ void del_gendisk(struct gendisk *disk)
sysfs_remove_link(&disk->driverfs_dev->kobj, "block");
put_device(disk->driverfs_dev);
}
kobject_get(&disk->kobj); /* kobject model is fucked in head */
kobject_unregister(&disk->kobj);
kobject_del(&disk->kobj);
}
struct dev_name {
......
......@@ -96,9 +96,10 @@ static int sysfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t
if (!dentry->d_inode) {
inode = sysfs_get_inode(dir->i_sb, mode, dev);
if (inode)
if (inode) {
d_instantiate(dentry, inode);
else
dget(dentry);
} else
error = -ENOSPC;
} else
error = -EEXIST;
......@@ -135,14 +136,52 @@ static int sysfs_symlink(struct inode * dir, struct dentry *dentry, const char *
if (inode) {
int l = strlen(symname)+1;
error = page_symlink(inode, symname, l);
if (!error)
if (!error) {
d_instantiate(dentry, inode);
else
dget(dentry);
} else
iput(inode);
}
return error;
}
#define to_subsys(k) container_of(k,struct subsystem,kobj)
#define to_sattr(a) container_of(a,struct subsys_attribute,attr)
/**
* Subsystem file operations.
* These operations allow subsystems to have files that can be
* read/written.
*/
ssize_t subsys_attr_show(struct kobject * kobj, struct attribute * attr,
char * page, size_t count, loff_t off)
{
struct subsystem * s = to_subsys(kobj);
struct subsys_attribute * sattr = to_sattr(attr);
ssize_t ret = 0;
if (sattr->show)
ret = sattr->show(s,page,count,off);
return ret;
}
ssize_t subsys_attr_store(struct kobject * kobj, struct attribute * attr,
const char * page, size_t count, loff_t off)
{
struct subsystem * s = to_subsys(kobj);
struct subsys_attribute * sattr = to_sattr(attr);
ssize_t ret = 0;
if (sattr->store)
ret = sattr->store(s,page,count,off);
return ret;
}
static struct sysfs_ops subsys_sysfs_ops = {
.show = subsys_attr_show,
.store = subsys_attr_store,
};
/**
* sysfs_read_file - read an attribute.
* @file: file pointer.
......@@ -263,9 +302,14 @@ static int check_perm(struct inode * inode, struct file * file)
if (!kobj || !attr)
goto Einval;
/* if the kobject has no subsystem, then it is a subsystem itself,
* so give it the subsys_sysfs_ops.
*/
if (kobj->subsys)
ops = kobj->subsys->sysfs_ops;
else
ops = &subsys_sysfs_ops;
/* No sysfs operations, either from having no subsystem,
* or the subsystem have no operations.
......@@ -571,8 +615,8 @@ static void hash_and_remove(struct dentry * dir, const char * name)
/* make sure dentry is really there */
if (victim->d_inode &&
(victim->d_parent->d_inode == dir->d_inode)) {
d_invalidate(victim);
simple_unlink(dir->d_inode,victim);
d_delete(victim);
}
}
up(&dir->d_inode->i_sem);
......@@ -631,15 +675,15 @@ void sysfs_remove_dir(struct kobject * kobj)
struct dentry * d = list_entry(node,struct dentry,d_child);
/* make sure dentry is still there */
if (d->d_inode) {
d_invalidate(d);
simple_unlink(dentry->d_inode,d);
d_delete(dentry);
}
}
up(&dentry->d_inode->i_sem);
d_invalidate(dentry);
simple_rmdir(parent->d_inode,dentry);
d_delete(dentry);
up(&parent->d_inode->i_sem);
dput(parent);
}
......
......@@ -85,6 +85,15 @@ extern struct bus_type * get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus);
/* iterator helpers for buses */
int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
/* driverfs interface for exporting bus attributes */
struct bus_attribute {
......@@ -159,6 +168,7 @@ struct device_class {
struct subsystem devsubsys;
struct subsystem drvsubsys;
struct list_head drivers;
struct list_head devices;
int (*add_device)(struct device *);
void (*remove_device)(struct device *);
......@@ -207,9 +217,7 @@ struct device_interface {
char * name;
struct device_class * devclass;
struct kobject kobj;
struct list_head devices;
struct subsystem subsys;
u32 devnum;
int (*add_device) (struct device *);
......@@ -230,10 +238,11 @@ extern void interface_unregister(struct device_interface *);
* and create a driverfs symlink for it.
*/
struct intf_data {
struct list_head node;
struct device_interface * intf;
struct device * dev;
u32 intf_num;
struct list_head dev_entry;
struct kobject kobj;
};
extern int interface_add_data(struct intf_data *);
......@@ -241,9 +250,9 @@ extern int interface_add_data(struct intf_data *);
struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */
struct list_head class_list;
struct list_head driver_list;
struct list_head children;
struct list_head intf_list;
......@@ -270,6 +279,7 @@ struct device {
being off. */
unsigned char *saved_state; /* saved device state */
u64 *dma_mask; /* dma mask (if dma'able device) */
void (*release)(struct device * dev);
};
......@@ -280,12 +290,6 @@ list_to_dev(struct list_head *node)
return list_entry(node, struct device, node);
}
static inline struct device *
g_list_to_dev(struct list_head *g_list)
{
return list_entry(g_list, struct device, g_list);
}
static inline void *
dev_get_drvdata (struct device *dev)
{
......
......@@ -60,4 +60,13 @@ static inline void subsys_put(struct subsystem * s)
kobject_put(&s->kobj);
}
struct subsys_attribute {
struct attribute attr;
ssize_t (*show)(struct subsystem *, char *, size_t, loff_t);
ssize_t (*store)(struct subsystem *, const char *, size_t, loff_t);
};
extern int subsys_create_file(struct subsystem * , struct subsys_attribute *);
extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *);
#endif /* _KOBJECT_H_ */
......@@ -48,13 +48,6 @@ extern void machine_restart(char *cmd);
extern void machine_halt(void);
extern void machine_power_off(void);
/*
* Architecture-independent suspend facility
*/
extern void software_suspend(void);
extern unsigned char software_suspend_enabled;
#endif
#endif /* _LINUX_REBOOT_H */
......@@ -21,6 +21,7 @@
#include <linux/times.h>
#include <linux/security.h>
#include <linux/dcookies.h>
#include <linux/suspend.h>
#include <asm/uaccess.h>
#include <asm/io.h>
......
......@@ -110,8 +110,7 @@ int kobject_register(struct kobject * kobj)
if (kobj) {
kobject_init(kobj);
error = kobject_add(kobj);
if (error)
kobject_cleanup(kobj);
WARN_ON(error);
} else
error = -EINVAL;
return error;
......@@ -229,6 +228,38 @@ void subsystem_unregister(struct subsystem * s)
}
/**
* subsystem_create_file - export sysfs attribute file.
* @s: subsystem.
* @a: subsystem attribute descriptor.
*/
int subsys_create_file(struct subsystem * s, struct subsys_attribute * a)
{
int error = 0;
if (subsys_get(s)) {
error = sysfs_create_file(&s->kobj,&a->attr);
subsys_put(s);
}
return error;
}
/**
* subsystem_remove_file - remove sysfs attribute file.
* @s: subsystem.
* @a: attribute desciptor.
*/
void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a)
{
if (subsys_get(s)) {
sysfs_remove_file(&s->kobj,&a->attr);
subsys_put(s);
}
}
EXPORT_SYMBOL(kobject_init);
EXPORT_SYMBOL(kobject_register);
EXPORT_SYMBOL(kobject_unregister);
......@@ -238,3 +269,5 @@ EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(subsystem_init);
EXPORT_SYMBOL(subsystem_register);
EXPORT_SYMBOL(subsystem_unregister);
EXPORT_SYMBOL(subsys_create_file);
EXPORT_SYMBOL(subsys_remove_file);
......@@ -22,5 +22,15 @@ config SECURITY_CAPABILITIES
This enables the "default" Linux capabilities functionality.
If you are unsure how to answer this question, answer Y.
config SECURITY_ROOTPLUG
tristate "Root Plug Support"
depends on SECURITY!=n
help
This is a sample LSM module that should only be used as such.
It enables control over processes being created by root users
if a specific USB device is not present in the system.
If you are unsure how to answer this question, answer N.
endmenu
......@@ -13,5 +13,6 @@ endif
# Object file lists
obj-$(CONFIG_SECURITY) += security.o dummy.o
obj-$(CONFIG_SECURITY_CAPABILITIES) += capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
include $(TOPDIR)/Rules.make
This diff is collapsed.
......@@ -12,6 +12,8 @@
* (at your option) any later version.
*/
#undef DEBUG
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
......@@ -19,6 +21,7 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
{
return 0;
......@@ -122,55 +125,55 @@ static int dummy_sb_statfs (struct super_block *sb)
return 0;
}
static int dummy_mount (char *dev_name, struct nameidata *nd, char *type,
unsigned long flags, void *data)
static int dummy_sb_mount (char *dev_name, struct nameidata *nd, char *type,
unsigned long flags, void *data)
{
return 0;
}
static int dummy_check_sb (struct vfsmount *mnt, struct nameidata *nd)
static int dummy_sb_check_sb (struct vfsmount *mnt, struct nameidata *nd)
{
return 0;
}
static int dummy_umount (struct vfsmount *mnt, int flags)
static int dummy_sb_umount (struct vfsmount *mnt, int flags)
{
return 0;
}
static void dummy_umount_close (struct vfsmount *mnt)
static void dummy_sb_umount_close (struct vfsmount *mnt)
{
return;
}
static void dummy_umount_busy (struct vfsmount *mnt)
static void dummy_sb_umount_busy (struct vfsmount *mnt)
{
return;
}
static void dummy_post_remount (struct vfsmount *mnt, unsigned long flags,
void *data)
static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags,
void *data)
{
return;
}
static void dummy_post_mountroot (void)
static void dummy_sb_post_mountroot (void)
{
return;
}
static void dummy_post_addmount (struct vfsmount *mnt, struct nameidata *nd)
static void dummy_sb_post_addmount (struct vfsmount *mnt, struct nameidata *nd)
{
return;
}
static int dummy_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
static int dummy_sb_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
{
return 0;
}
static void dummy_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd)
{
return;
}
......@@ -303,12 +306,12 @@ static int dummy_inode_getattr (struct vfsmount *mnt, struct dentry *dentry)
return 0;
}
static void dummy_post_lookup (struct inode *ino, struct dentry *d)
static void dummy_inode_post_lookup (struct inode *ino, struct dentry *d)
{
return;
}
static void dummy_delete (struct inode *ino)
static void dummy_inode_delete (struct inode *ino)
{
return;
}
......@@ -529,12 +532,12 @@ static void dummy_sem_free_security (struct sem_array *sma)
return;
}
static int dummy_register (const char *name, struct security_operations *ops)
static int dummy_register_security (const char *name, struct security_operations *ops)
{
return -EINVAL;
}
static int dummy_unregister (const char *name, struct security_operations *ops)
static int dummy_unregister_security (const char *name, struct security_operations *ops)
{
return -EINVAL;
}
......@@ -558,16 +561,16 @@ struct security_operations dummy_security_ops = {
.sb_alloc_security = dummy_sb_alloc_security,
.sb_free_security = dummy_sb_free_security,
.sb_statfs = dummy_sb_statfs,
.sb_mount = dummy_mount,
.sb_check_sb = dummy_check_sb,
.sb_umount = dummy_umount,
.sb_umount_close = dummy_umount_close,
.sb_umount_busy = dummy_umount_busy,
.sb_post_remount = dummy_post_remount,
.sb_post_mountroot = dummy_post_mountroot,
.sb_post_addmount = dummy_post_addmount,
.sb_pivotroot = dummy_pivotroot,
.sb_post_pivotroot = dummy_post_pivotroot,
.sb_mount = dummy_sb_mount,
.sb_check_sb = dummy_sb_check_sb,
.sb_umount = dummy_sb_umount,
.sb_umount_close = dummy_sb_umount_close,
.sb_umount_busy = dummy_sb_umount_busy,
.sb_post_remount = dummy_sb_post_remount,
.sb_post_mountroot = dummy_sb_post_mountroot,
.sb_post_addmount = dummy_sb_post_addmount,
.sb_pivotroot = dummy_sb_pivotroot,
.sb_post_pivotroot = dummy_sb_post_pivotroot,
.inode_alloc_security = dummy_inode_alloc_security,
.inode_free_security = dummy_inode_free_security,
......@@ -591,8 +594,8 @@ struct security_operations dummy_security_ops = {
.inode_permission_lite = dummy_inode_permission_lite,
.inode_setattr = dummy_inode_setattr,
.inode_getattr = dummy_inode_getattr,
.inode_post_lookup = dummy_post_lookup,
.inode_delete = dummy_delete,
.inode_post_lookup = dummy_inode_post_lookup,
.inode_delete = dummy_inode_delete,
.inode_setxattr = dummy_inode_setxattr,
.inode_getxattr = dummy_inode_getxattr,
.inode_listxattr = dummy_inode_listxattr,
......@@ -641,7 +644,113 @@ struct security_operations dummy_security_ops = {
.sem_alloc_security = dummy_sem_alloc_security,
.sem_free_security = dummy_sem_free_security,
.register_security = dummy_register,
.unregister_security = dummy_unregister,
.register_security = dummy_register_security,
.unregister_security = dummy_unregister_security,
};
#define set_to_dummy_if_null(ops, function) \
do { \
if (!ops->function) { \
ops->function = dummy_##function; \
pr_debug("Had to override the " #function \
" security operation with the dummy one.\n");\
} \
} while (0)
void security_fixup_ops (struct security_operations *ops)
{
set_to_dummy_if_null(ops, ptrace);
set_to_dummy_if_null(ops, capget);
set_to_dummy_if_null(ops, capset_check);
set_to_dummy_if_null(ops, capset_set);
set_to_dummy_if_null(ops, acct);
set_to_dummy_if_null(ops, capable);
set_to_dummy_if_null(ops, quotactl);
set_to_dummy_if_null(ops, quota_on);
set_to_dummy_if_null(ops, bprm_alloc_security);
set_to_dummy_if_null(ops, bprm_free_security);
set_to_dummy_if_null(ops, bprm_compute_creds);
set_to_dummy_if_null(ops, bprm_set_security);
set_to_dummy_if_null(ops, bprm_check_security);
set_to_dummy_if_null(ops, sb_alloc_security);
set_to_dummy_if_null(ops, sb_free_security);
set_to_dummy_if_null(ops, sb_statfs);
set_to_dummy_if_null(ops, sb_mount);
set_to_dummy_if_null(ops, sb_check_sb);
set_to_dummy_if_null(ops, sb_umount);
set_to_dummy_if_null(ops, sb_umount_close);
set_to_dummy_if_null(ops, sb_umount_busy);
set_to_dummy_if_null(ops, sb_post_remount);
set_to_dummy_if_null(ops, sb_post_mountroot);
set_to_dummy_if_null(ops, sb_post_addmount);
set_to_dummy_if_null(ops, sb_pivotroot);
set_to_dummy_if_null(ops, sb_post_pivotroot);
set_to_dummy_if_null(ops, inode_alloc_security);
set_to_dummy_if_null(ops, inode_free_security);
set_to_dummy_if_null(ops, inode_create);
set_to_dummy_if_null(ops, inode_post_create);
set_to_dummy_if_null(ops, inode_link);
set_to_dummy_if_null(ops, inode_post_link);
set_to_dummy_if_null(ops, inode_unlink);
set_to_dummy_if_null(ops, inode_symlink);
set_to_dummy_if_null(ops, inode_post_symlink);
set_to_dummy_if_null(ops, inode_mkdir);
set_to_dummy_if_null(ops, inode_post_mkdir);
set_to_dummy_if_null(ops, inode_rmdir);
set_to_dummy_if_null(ops, inode_mknod);
set_to_dummy_if_null(ops, inode_post_mknod);
set_to_dummy_if_null(ops, inode_rename);
set_to_dummy_if_null(ops, inode_post_rename);
set_to_dummy_if_null(ops, inode_readlink);
set_to_dummy_if_null(ops, inode_follow_link);
set_to_dummy_if_null(ops, inode_permission);
set_to_dummy_if_null(ops, inode_permission_lite);
set_to_dummy_if_null(ops, inode_setattr);
set_to_dummy_if_null(ops, inode_getattr);
set_to_dummy_if_null(ops, inode_post_lookup);
set_to_dummy_if_null(ops, inode_delete);
set_to_dummy_if_null(ops, inode_setxattr);
set_to_dummy_if_null(ops, inode_getxattr);
set_to_dummy_if_null(ops, inode_listxattr);
set_to_dummy_if_null(ops, inode_removexattr);
set_to_dummy_if_null(ops, file_permission);
set_to_dummy_if_null(ops, file_alloc_security);
set_to_dummy_if_null(ops, file_free_security);
set_to_dummy_if_null(ops, file_ioctl);
set_to_dummy_if_null(ops, file_mmap);
set_to_dummy_if_null(ops, file_mprotect);
set_to_dummy_if_null(ops, file_lock);
set_to_dummy_if_null(ops, file_fcntl);
set_to_dummy_if_null(ops, file_set_fowner);
set_to_dummy_if_null(ops, file_send_sigiotask);
set_to_dummy_if_null(ops, file_receive);
set_to_dummy_if_null(ops, task_create);
set_to_dummy_if_null(ops, task_alloc_security);
set_to_dummy_if_null(ops, task_free_security);
set_to_dummy_if_null(ops, task_setuid);
set_to_dummy_if_null(ops, task_post_setuid);
set_to_dummy_if_null(ops, task_setgid);
set_to_dummy_if_null(ops, task_setpgid);
set_to_dummy_if_null(ops, task_getpgid);
set_to_dummy_if_null(ops, task_getsid);
set_to_dummy_if_null(ops, task_setgroups);
set_to_dummy_if_null(ops, task_setnice);
set_to_dummy_if_null(ops, task_setrlimit);
set_to_dummy_if_null(ops, task_setscheduler);
set_to_dummy_if_null(ops, task_getscheduler);
set_to_dummy_if_null(ops, task_wait);
set_to_dummy_if_null(ops, task_kill);
set_to_dummy_if_null(ops, task_prctl);
set_to_dummy_if_null(ops, task_kmod_set_label);
set_to_dummy_if_null(ops, task_reparent_to_init);
set_to_dummy_if_null(ops, ipc_permission);
set_to_dummy_if_null(ops, msg_queue_alloc_security);
set_to_dummy_if_null(ops, msg_queue_free_security);
set_to_dummy_if_null(ops, shm_alloc_security);
set_to_dummy_if_null(ops, shm_free_security);
set_to_dummy_if_null(ops, sem_alloc_security);
set_to_dummy_if_null(ops, sem_free_security);
set_to_dummy_if_null(ops, register_security);
set_to_dummy_if_null(ops, unregister_security);
}
/*
* Root Plug sample LSM module
*
* Originally written for a Linux Journal.
*
* Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.com>
*
* Prevents any programs running with egid == 0 if a specific USB device
* is not present in the system. Yes, it can be gotten around, but is a
* nice starting point for people to play with, and learn the LSM
* interface.
*
* If you want to turn this into something with a semblance of security,
* you need to hook the task_* functions also.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/security.h>
#include <linux/usb.h>
/* flag to keep track of how we were registered */
static int secondary;
/* default is a generic type of usb to serial converter */
static int vendor_id = 0x0557;
static int product_id = 0x2008;
MODULE_PARM(vendor_id, "h");
MODULE_PARM_DESC(vendor_id, "USB Vendor ID of device to look for");
MODULE_PARM(product_id, "h");
MODULE_PARM_DESC(product_id, "USB Product ID of device to look for");
/* should we print out debug messages */
static int debug = 0;
MODULE_PARM(debug, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
#if defined(CONFIG_SECURITY_ROOTPLUG_MODULE)
#define MY_NAME THIS_MODULE->name
#else
#define MY_NAME "root_plug"
#endif
#define dbg(fmt, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "%s: %s: " fmt , \
MY_NAME , __FUNCTION__ , \
## arg); \
} while (0)
extern struct list_head usb_bus_list;
extern struct semaphore usb_bus_list_lock;
static int match_device (struct usb_device *dev)
{
int retval = -ENODEV;
int child;
dbg ("looking at vendor %d, product %d\n",
dev->descriptor.idVendor,
dev->descriptor.idProduct);
/* see if this device matches */
if ((dev->descriptor.idVendor == vendor_id) &&
(dev->descriptor.idProduct == product_id)) {
dbg ("found the device!\n");
retval = 0;
goto exit;
}
/* look through all of the children of this device */
for (child = 0; child < dev->maxchild; ++child) {
if (dev->children[child]) {
retval = match_device (dev->children[child]);
if (retval == 0)
goto exit;
}
}
exit:
return retval;
}
static int find_usb_device (void)
{
struct list_head *buslist;
struct usb_bus *bus;
int retval = -ENODEV;
down (&usb_bus_list_lock);
for (buslist = usb_bus_list.next;
buslist != &usb_bus_list;
buslist = buslist->next) {
bus = container_of (buslist, struct usb_bus, bus_list);
retval = match_device(bus->root_hub);
if (retval == 0)
goto exit;
}
exit:
up (&usb_bus_list_lock);
return retval;
}
static int rootplug_bprm_check_security (struct linux_binprm *bprm)
{
dbg ("file %s, e_uid = %d, e_gid = %d\n",
bprm->filename, bprm->e_uid, bprm->e_gid);
if (bprm->e_gid == 0) {
if (find_usb_device() != 0) {
dbg ("e_gid = 0, and device not found, "
"task not allowed to run...\n");
return -EPERM;
}
}
return 0;
}
static struct security_operations rootplug_security_ops = {
/* Use the capability functions for some of the hooks */
.ptrace = cap_ptrace,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,
.capable = cap_capable,
.bprm_compute_creds = cap_bprm_compute_creds,
.bprm_set_security = cap_bprm_set_security,
.task_post_setuid = cap_task_post_setuid,
.task_kmod_set_label = cap_task_kmod_set_label,
.task_reparent_to_init = cap_task_reparent_to_init,
.bprm_check_security = rootplug_bprm_check_security,
};
static int __init rootplug_init (void)
{
/* register ourselves with the security framework */
if (register_security (&rootplug_security_ops)) {
printk (KERN_INFO
"Failure registering Root Plug module with the kernel\n");
/* try registering with primary module */
if (mod_reg_security (MY_NAME, &rootplug_security_ops)) {
printk (KERN_INFO "Failure registering Root Plug "
" module with primary security module.\n");
return -EINVAL;
}
secondary = 1;
}
printk (KERN_INFO "Root Plug module initialized, "
"vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id);
return 0;
}
static void __exit rootplug_exit (void)
{
/* remove ourselves from the security framework */
if (secondary) {
if (mod_unreg_security (MY_NAME, &rootplug_security_ops))
printk (KERN_INFO "Failure unregistering Root Plug "
" module with primary module.\n");
} else {
if (unregister_security (&rootplug_security_ops)) {
printk (KERN_INFO "Failure unregistering Root Plug "
"module with the kernel\n");
}
}
printk (KERN_INFO "Root Plug module removed\n");
}
module_init (rootplug_init);
module_exit (rootplug_exit);
MODULE_DESCRIPTION("Root Plug sample LSM module, written for Linux Journal article");
MODULE_LICENSE("GPL");
......@@ -20,59 +20,21 @@
#define SECURITY_SCAFFOLD_VERSION "1.0.0"
extern struct security_operations dummy_security_ops; /* lives in dummy.c */
/* things that live in dummy.c */
extern struct security_operations dummy_security_ops;
extern void security_fixup_ops (struct security_operations *ops);
struct security_operations *security_ops; /* Initialized to NULL */
/* This macro checks that all pointers in a struct are non-NULL. It
* can be fooled by struct padding for object tile alignment and when
* pointers to data and pointers to functions aren't the same size.
* Yes it's ugly, we'll replace it if it becomes a problem.
*/
#define VERIFY_STRUCT(struct_type, s, e) \
do { \
unsigned long * __start = (unsigned long *)(s); \
unsigned long * __end = __start + \
sizeof(struct_type)/sizeof(unsigned long *); \
while (__start != __end) { \
if (!*__start) { \
printk(KERN_INFO "%s is missing something\n",\
#struct_type); \
e++; \
break; \
} \
__start++; \
} \
} while (0)
static int inline verify (struct security_operations *ops)
static inline int verify (struct security_operations *ops)
{
int err;
/* verify the security_operations structure exists */
if (!ops) {
printk (KERN_INFO "Passed a NULL security_operations "
"pointer, %s failed.\n", __FUNCTION__);
return -EINVAL;
}
/* Perform a little sanity checking on our inputs */
err = 0;
/* This first check scans the whole security_ops struct for
* missing structs or functions.
*
* (There is no further check now, but will leave as is until
* the lazy registration stuff is done -- JM).
*/
VERIFY_STRUCT(struct security_operations, ops, err);
if (err) {
printk (KERN_INFO "Not enough functions specified in the "
"security_operation structure, %s failed.\n",
__FUNCTION__);
return -EINVAL;
}
security_fixup_ops (ops);
return 0;
}
......@@ -106,12 +68,12 @@ int security_scaffolding_startup (void)
*/
int register_security (struct security_operations *ops)
{
if (verify (ops)) {
printk (KERN_INFO "%s could not verify "
"security_operations structure.\n", __FUNCTION__);
return -EINVAL;
}
if (security_ops != &dummy_security_ops) {
printk (KERN_INFO "There is already a security "
"framework initialized, %s failed.\n", __FUNCTION__);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment