static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, const char *name, int warn) { struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; struct sysfs_addrm_cxt acxt; int error; BUG_ON(!name); if (!kobj) parent_sd = &sysfs_root; else parent_sd = kobj->sd; error = -EFAULT; if (!parent_sd) goto out_put; /* target->sd can go away beneath us but is protected with * sysfs_assoc_lock. Fetch target_sd from it. */ spin_lock(&sysfs_assoc_lock); if (target->sd) target_sd = sysfs_get(target->sd); spin_unlock(&sysfs_assoc_lock); error = -ENOENT; if (!target_sd) goto out_put; error = -ENOMEM; sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto out_put; sd->s_symlink.target_sd = target_sd; target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); if (warn) error = sysfs_add_one(&acxt, sd); else error = __sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (error) goto out_put; return 0; out_put: sysfs_put(target_sd); sysfs_put(sd); return error; }
static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, const char *name, struct sysfs_dirent **p_sd) { umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; /* allocate */ sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) return -ENOMEM; sd->s_elem.dir.kobj = kobj; /* link in */ sysfs_addrm_start(&acxt, parent_sd); if (!sysfs_find_dirent(parent_sd, name)) { sysfs_add_one(&acxt, sd); sysfs_link_sibling(sd); } if (!sysfs_addrm_finish(&acxt)) { sysfs_put(sd); return -EEXIST; } *p_sd = sd; return 0; }
static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) { struct sysfs_dirent * sd = dentry->d_fsdata; sysfs_put(sd); iput(inode); }
/** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * @mode: file permissions. * */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { struct sysfs_dirent *victim_sd = NULL; struct dentry *victim = NULL; struct inode * inode; struct iattr newattrs; int rc; rc = -ENOENT; victim_sd = sysfs_get_dirent(kobj->sd, attr->name); if (!victim_sd) goto out; victim = sysfs_get_dentry(victim_sd); if (IS_ERR(victim)) { rc = PTR_ERR(victim); victim = NULL; goto out; } inode = victim->d_inode; mutex_lock(&inode->i_mutex); newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; rc = notify_change(victim, &newattrs); mutex_unlock(&inode->i_mutex); out: dput(victim); sysfs_put(victim_sd); return rc; }
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { struct sysfs_dirent *sd; int error; BUG_ON(!kobj || (!update && !kobj->sd)); /* Updates may happen before the object has been instantiated */ if (unlikely(update && !kobj->sd)) return -EINVAL; if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else sd = kobj->sd; sysfs_get(sd); error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) sysfs_remove_subdir(sd); } sysfs_put(sd); return error; }
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type) { umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) return -ENOMEM; sd->s_elem.attr.attr = (void *)attr; sysfs_addrm_start(&acxt, dir_sd); if (!sysfs_find_dirent(dir_sd, attr->name)) { sysfs_add_one(&acxt, sd); sysfs_link_sibling(sd); } if (!sysfs_addrm_finish(&acxt)) { sysfs_put(sd); return -EEXIST; } return 0; }
/** * sysfs_addrm_finish - finish up sysfs_dirent add/remove * @acxt: addrm context to finish up * * Finish up sysfs_dirent add/remove. Resources acquired by * sysfs_addrm_start() are released and removed sysfs_dirents are * cleaned up. Timestamps on the parent inode are updated. * * LOCKING: * All mutexes acquired by sysfs_addrm_start() are released. * * RETURNS: * Number of added/removed sysfs_dirents since sysfs_addrm_start(). */ int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) { /* release resources acquired by sysfs_addrm_start() */ mutex_unlock(&sysfs_mutex); if (acxt->parent_inode) { struct inode *inode = acxt->parent_inode; /* if added/removed, update timestamps on the parent */ if (acxt->cnt) inode->i_ctime = inode->i_mtime = CURRENT_TIME; mutex_unlock(&inode->i_mutex); iput(inode); } /* kill removed sysfs_dirents */ while (acxt->removed) { struct sysfs_dirent *sd = acxt->removed; acxt->removed = sd->s_sibling; sd->s_sibling = NULL; sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); } return acxt->cnt; }
static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, enum kobj_ns_type type, const char *name, const void *ns, struct sysfs_dirent **p_sd) { umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; /* allocate */ sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) return -ENOMEM; sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); sd->s_ns = ns; sd->s_dir.kobj = kobj; /* link in */ sysfs_addrm_start(&acxt); rc = sysfs_add_one(&acxt, sd, parent_sd); sysfs_addrm_finish(&acxt); if (rc == 0) *p_sd = sd; else sysfs_put(sd); return rc; }
static struct sysfs_dirent *sysfs_dir_pos(const void *ns, struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) { if (pos) { int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && pos->s_parent == parent_sd && hash == pos->s_hash; sysfs_put(pos); if (!valid) pos = NULL; } if (!pos && (hash > 1) && (hash < INT_MAX)) { struct rb_node *node = parent_sd->s_dir.children.rb_node; while (node) { pos = to_sysfs_dirent(node); if (hash < pos->s_hash) node = node->rb_left; else if (hash > pos->s_hash) node = node->rb_right; else break; } } /* Skip over entries in the wrong namespace */ while (pos && pos->s_ns != ns) { struct rb_node *node = rb_next(&pos->s_rb); if (!node) pos = NULL; else pos = to_sysfs_dirent(node); } return pos; }
void release_sysfs_dirent(struct sysfs_dirent *sd) { struct sysfs_dirent *parent_sd; repeat: /* Moving/renaming is always done while holding reference. * sd->s_parent won't change beneath us. */ parent_sd = sd->s_parent; WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), "sysfs: free using entry: %s/%s\n", parent_sd ? parent_sd->s_name : "", sd->s_name); if (sysfs_type(sd) == SYSFS_KOBJ_LINK) sysfs_put(sd->s_symlink.target_sd); if (sysfs_type(sd) & SYSFS_COPY_NAME) kfree(sd->s_name); if (sd->s_iattr && sd->s_iattr->ia_secdata) security_release_secctx(sd->s_iattr->ia_secdata, sd->s_iattr->ia_secdata_len); kfree(sd->s_iattr); sysfs_free_ino(sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); sd = parent_sd; if (sd && atomic_dec_and_test(&sd->s_count)) goto repeat; }
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { struct sysfs_dirent *sd; int error; BUG_ON(!kobj || (!update && !kobj->sd)); /* Updates may happen before the object has been instantiated */ if (unlikely(update && !kobj->sd)) return -EINVAL; if (!grp->attrs && !grp->bin_attrs) { WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n", kobj->name, grp->name ? "" : grp->name); return -EINVAL; } if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else sd = kobj->sd; sysfs_get(sd); error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) sysfs_remove_subdir(sd); } sysfs_put(sd); return error; }
/** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { struct sysfs_dirent *victim_sd = NULL; struct dentry *victim = NULL; int rc; rc = -ENOENT; victim_sd = sysfs_get_dirent(kobj->sd, attr->name); if (!victim_sd) goto out; victim = sysfs_get_dentry(victim_sd); if (IS_ERR(victim)) { rc = PTR_ERR(victim); victim = NULL; goto out; } mutex_lock(&victim->d_inode->i_mutex); victim->d_inode->i_mtime = CURRENT_TIME; fsnotify_modify(victim); mutex_unlock(&victim->d_inode->i_mutex); rc = 0; out: dput(victim); sysfs_put(victim_sd); return rc; }
/* * The sysfs_dirent serves as both an inode and a directory entry for sysfs. * To prevent the sysfs inode numbers from being freed prematurely we take a * reference to sysfs_dirent from the sysfs inode. A * super_operations.delete_inode() implementation is needed to drop that * reference upon inode destruction. */ void sysfs_delete_inode(struct inode *inode) { struct sysfs_dirent *sd = inode->i_private; truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); sysfs_put(sd); }
/* * The sysfs_dirent serves as both an inode and a directory entry for sysfs. * To prevent the sysfs inode numbers from being freed prematurely we take a * reference to sysfs_dirent from the sysfs inode. A * super_operations.evict_inode() implementation is needed to drop that * reference upon inode destruction. */ void sysfs_evict_inode(struct inode *inode) { struct sysfs_dirent *sd = inode->i_private; truncate_inode_pages(&inode->i_data, 0); end_writeback(inode); sysfs_put(sd); }
/** * sysfs_remove_file_from_group - remove an attribute file from a group. * @kobj: object we're acting for. * @attr: attribute descriptor. * @group: group name. */ void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group) { struct sysfs_dirent *dir_sd; dir_sd = sysfs_get_dirent(kobj->sd, group); if (dir_sd) { sysfs_hash_and_remove(dir_sd, attr->name); sysfs_put(dir_sd); } }
/** * sysfs_remove_link_from_group - remove a symlink from an attribute group. * @kobj: The kobject containing the group. * @group_name: The name of the group. * @link_name: The name of the symlink to remove. */ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name) { struct sysfs_dirent *dir_sd; dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); if (dir_sd) { sysfs_hash_and_remove(dir_sd, NULL, link_name); sysfs_put(dir_sd); } }
static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) { struct sysfs_dirent * sd = dentry->d_fsdata; if (sd) { BUG_ON(sd->s_dentry != dentry); sd->s_dentry = NULL; sysfs_put(sd); } iput(inode); }
static void mic_remove(struct pci_dev *pdev) { int32_t brdnum; bd_info_t *bd_info; if (mic_data.dd_numdevs - 1 < 0) return; mic_data.dd_numdevs--; brdnum = mic_data.dd_numdevs; /* Make sure boards are shutdown and not available. */ bd_info = mic_data.dd_bi[brdnum]; spin_lock_bh(&bd_info->bi_ctx.sysfs_lock); sysfs_put(bd_info->bi_ctx.sysfs_state); bd_info->bi_ctx.sysfs_state = NULL; spin_unlock_bh(&bd_info->bi_ctx.sysfs_lock); if (bd_info->bi_ctx.bi_psmi.enabled) { device_remove_bin_file(bd_info->bi_sysfsdev, &mic_psmi_ptes_attr); sysfs_remove_group(&bd_info->bi_sysfsdev->kobj, &psmi_attr_group); } sysfs_remove_group(&bd_info->bi_sysfsdev->kobj, &bd_attr_group); free_sysfs_entries(&bd_info->bi_ctx); device_destroy(mic_lindata.dd_class, mic_lindata.dd_dev + 2 + bd_info->bi_ctx.bi_id); adapter_stop_device(&bd_info->bi_ctx, 1, 0); /* * Need to wait for reset since accessing the card while GDDR training * is ongoing by adapter_remove(..) below for example can be fatal. */ wait_for_reset(&bd_info->bi_ctx); mic_disable_interrupts(&bd_info->bi_ctx); if (!bd_info->bi_ctx.msie) { free_irq(bd_info->bi_ctx.bi_pdev->irq, &bd_info->bi_ctx); #ifdef CONFIG_PCI_MSI } else { free_irq(bd_info->bi_msix_entries[0].vector, &bd_info->bi_ctx); pci_disable_msix(bd_info->bi_ctx.bi_pdev); #endif } adapter_remove(&bd_info->bi_ctx); release_mem_region(bd_info->bi_ctx.aper.pa, bd_info->bi_ctx.aper.len); release_mem_region(bd_info->bi_ctx.mmio.pa, bd_info->bi_ctx.mmio.len); pci_disable_device(bd_info->bi_ctx.bi_pdev); kfree(bd_info); }
/** * sysfs_unmerge_group - remove files from a pre-existing attribute group. * @kobj: The kobject containing the group. * @grp: The files to remove and the attribute group they belong to. */ void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) { struct sysfs_dirent *dir_sd; struct attribute *const *attr; dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); if (dir_sd) { for (attr = grp->attrs; *attr; ++attr) sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); sysfs_put(dir_sd); } }
static int sysfs_release(struct inode * inode, struct file * filp) { struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct sysfs_buffer *buffer = filp->private_data; sysfs_put(attr_sd); if (buffer) { if (buffer->page) free_page((unsigned long)buffer->page); kfree(buffer); } return 0; }
/** * sysfs_add_link_to_group - add a symlink to an attribute group. * @kobj: The kobject containing the group. * @group_name: The name of the group. * @target: The target kobject of the symlink to create. * @link_name: The name of the symlink to create. */ int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, struct kobject *target, const char *link_name) { struct sysfs_dirent *dir_sd; int error = 0; dir_sd = sysfs_get_dirent(kobj->sd, NULL, group_name); if (!dir_sd) return -ENOENT; error = sysfs_create_link_sd(dir_sd, target, link_name); sysfs_put(dir_sd); return error; }
/** * sysfs_add_file_to_group - add an attribute file to a pre-existing group. * @kobj: object we're acting for. * @attr: attribute descriptor. * @group: group name. */ int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { struct sysfs_dirent *dir_sd; int error; dir_sd = sysfs_get_dirent(kobj->sd, group); if (!dir_sd) return -ENOENT; error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); sysfs_put(dir_sd); return error; }
/** * sysfs_addrm_finish - finish up sysfs_dirent add/remove * @acxt: addrm context to finish up * * Finish up sysfs_dirent add/remove. Resources acquired by * sysfs_addrm_start() are released and removed sysfs_dirents are * cleaned up. * * LOCKING: * sysfs_mutex is released. */ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) { /* release resources acquired by sysfs_addrm_start() */ mutex_unlock(&sysfs_mutex); /* kill removed sysfs_dirents */ while (acxt->removed) { struct sysfs_dirent *sd = acxt->removed; acxt->removed = sd->s_sibling; sd->s_sibling = NULL; sysfs_deactivate(sd); unmap_bin_file(sd); sysfs_put(sd); } }
void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { struct sysfs_dirent *dir_sd = kobj->sd; struct sysfs_dirent *sd; if (grp->name) { sd = sysfs_get_dirent(dir_sd, grp->name); BUG_ON(!sd); } else sd = sysfs_get(dir_sd); remove_files(sd, grp); if (grp->name) sysfs_remove_subdir(sd); sysfs_put(sd); }
static struct sysfs_dirent *sysfs_dir_pos(struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) { if (pos) { int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && pos->s_parent == parent_sd && ino == pos->s_ino; sysfs_put(pos); if (valid) return pos; } pos = NULL; if ((ino > 1) && (ino < INT_MAX)) { pos = parent_sd->s_dir.children; while (pos && (ino > pos->s_ino)) pos = pos->s_sibling; } return pos; }
int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, const char *new_name, const void *new_ns) { int error; mutex_lock(&sysfs_mutex); error = 0; if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; if (sysfs_find_dirent(new_parent_sd, new_name, new_ns)) goto out; /* rename sysfs_dirent */ if (strcmp(sd->s_name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; kfree(sd->s_name); sd->s_name = new_name; } /* * Move to the appropriate place in the appropriate directories rbtree. */ sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_ns = new_ns; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); error = 0; out: mutex_unlock(&sysfs_mutex); return error; }
int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, const char *new_name) { const char *dup_name = NULL; int error; mutex_lock(&sysfs_mutex); error = 0; if ((sd->s_parent == new_parent_sd) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; if (sysfs_find_dirent(new_parent_sd, new_name)) goto out; /* rename sysfs_dirent */ if (strcmp(sd->s_name, new_name) != 0) { error = -ENOMEM; new_name = dup_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; dup_name = sd->s_name; sd->s_name = new_name; } /* Remove from old parent's list and insert into new parent's list. */ if (sd->s_parent != new_parent_sd) { sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); } error = 0; out: mutex_unlock(&sysfs_mutex); kfree(dup_name); return error; }
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type) { umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) return -ENOMEM; sd->s_attr.attr = (void *)attr; sysfs_addrm_start(&acxt, dir_sd); rc = sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (rc) sysfs_put(sd); return rc; }
/** * sysfs_merge_group - merge files into a pre-existing attribute group. * @kobj: The kobject containing the group. * @grp: The files to create and the attribute group they belong to. * * This function returns an error if the group doesn't exist or any of the * files already exist in that group, in which case none of the new files * are created. */ int sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp) { struct sysfs_dirent *dir_sd; int error = 0; struct attribute *const *attr; int i; dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name); if (!dir_sd) return -ENOENT; for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); if (error) { while (--i >= 0) sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name); } sysfs_put(dir_sd); return error; }
void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { struct sysfs_dirent *dir_sd = kobj->sd; struct sysfs_dirent *sd; if (grp->name) { sd = sysfs_get_dirent(dir_sd, NULL, grp->name); if (!sd) { WARN(!sd, KERN_WARNING "sysfs group %p not found for " "kobject '%s'\n", grp, kobject_name(kobj)); return; } } else sd = sysfs_get(dir_sd); remove_files(sd, kobj, grp); if (grp->name) sysfs_remove_subdir(sd); sysfs_put(sd); }