LCOV - code coverage report
Current view: top level - fs/sysfs - file.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 54 156 34.6 %
Date: 2014-04-16 Functions: 11 19 57.9 %
Branches: 36 113 31.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * fs/sysfs/file.c - sysfs regular (text) file implementation
       3                 :            :  *
       4                 :            :  * Copyright (c) 2001-3 Patrick Mochel
       5                 :            :  * Copyright (c) 2007 SUSE Linux Products GmbH
       6                 :            :  * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
       7                 :            :  *
       8                 :            :  * This file is released under the GPLv2.
       9                 :            :  *
      10                 :            :  * Please see Documentation/filesystems/sysfs.txt for more information.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include <linux/module.h>
      14                 :            : #include <linux/kobject.h>
      15                 :            : #include <linux/kallsyms.h>
      16                 :            : #include <linux/slab.h>
      17                 :            : #include <linux/list.h>
      18                 :            : #include <linux/mutex.h>
      19                 :            : #include <linux/seq_file.h>
      20                 :            : 
      21                 :            : #include "sysfs.h"
      22                 :            : #include "../kernfs/kernfs-internal.h"
      23                 :            : 
      24                 :            : /*
      25                 :            :  * Determine ktype->sysfs_ops for the given kernfs_node.  This function
      26                 :            :  * must be called while holding an active reference.
      27                 :            :  */
      28                 :            : static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
      29                 :            : {
      30                 :        176 :         struct kobject *kobj = kn->parent->priv;
      31                 :            : 
      32                 :            :         if (kn->flags & KERNFS_LOCKDEP)
      33                 :            :                 lockdep_assert_held(kn);
      34 [ +  - ][ +  + ]:       1625 :         return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
      35                 :            : }
      36                 :            : 
      37                 :            : /*
      38                 :            :  * Reads on sysfs are handled through seq_file, which takes care of hairy
      39                 :            :  * details like buffering and seeking.  The following function pipes
      40                 :            :  * sysfs_ops->show() result through seq_file.
      41                 :            :  */
      42                 :          0 : static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
      43                 :            : {
      44                 :       1449 :         struct kernfs_open_file *of = sf->private;
      45                 :       1449 :         struct kobject *kobj = of->kn->parent->priv;
      46                 :            :         const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
      47                 :            :         ssize_t count;
      48                 :            :         char *buf;
      49                 :            : 
      50                 :            :         /* acquire buffer and ensure that it's >= PAGE_SIZE */
      51                 :       1449 :         count = seq_get_buf(sf, &buf);
      52         [ -  + ]:       1449 :         if (count < PAGE_SIZE) {
      53                 :            :                 seq_commit(sf, -1);
      54                 :          0 :                 return 0;
      55                 :            :         }
      56                 :            : 
      57                 :            :         /*
      58                 :            :          * Invoke show().  Control may reach here via seq file lseek even
      59                 :            :          * if @ops->show() isn't implemented.
      60                 :            :          */
      61         [ +  - ]:       1449 :         if (ops->show) {
      62                 :       1449 :                 count = ops->show(kobj, of->kn->priv, buf);
      63            [ + ]:       1447 :                 if (count < 0)
      64                 :            :                         return count;
      65                 :            :         }
      66                 :            : 
      67                 :            :         /*
      68                 :            :          * The code works fine with PAGE_SIZE return but it's likely to
      69                 :            :          * indicate truncated result or overflow in normal use cases.
      70                 :            :          */
      71         [ -  + ]:       2896 :         if (count >= (ssize_t)PAGE_SIZE) {
      72                 :            :                 print_symbol("fill_read_buffer: %s returned bad count\n",
      73                 :          0 :                         (unsigned long)ops->show);
      74                 :            :                 /* Try to struggle along */
      75                 :            :                 count = PAGE_SIZE - 1;
      76                 :            :         }
      77                 :            :         seq_commit(sf, count);
      78                 :            :         return 0;
      79                 :            : }
      80                 :            : 
      81                 :          0 : static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
      82                 :            :                                  size_t count, loff_t pos)
      83                 :            : {
      84                 :          8 :         struct bin_attribute *battr = of->kn->priv;
      85                 :          8 :         struct kobject *kobj = of->kn->parent->priv;
      86                 :          8 :         loff_t size = file_inode(of->file)->i_size;
      87                 :            : 
      88         [ +  - ]:          8 :         if (!count)
      89                 :            :                 return 0;
      90                 :            : 
      91         [ +  - ]:          8 :         if (size) {
      92         [ +  - ]:          8 :                 if (pos > size)
      93                 :            :                         return 0;
      94         [ -  + ]:          8 :                 if (pos + count > size)
      95                 :          0 :                         count = size - pos;
      96                 :            :         }
      97                 :            : 
      98         [ +  - ]:          8 :         if (!battr->read)
      99                 :            :                 return -EIO;
     100                 :            : 
     101                 :          8 :         return battr->read(of->file, kobj, battr, buf, pos, count);
     102                 :            : }
     103                 :            : 
     104                 :            : /* kernfs write callback for regular sysfs files */
     105                 :          0 : static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
     106                 :            :                               size_t count, loff_t pos)
     107                 :            : {
     108                 :        176 :         const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
     109                 :            :         struct kobject *kobj = of->kn->parent->priv;
     110                 :            : 
     111         [ +  - ]:        176 :         if (!count)
     112                 :            :                 return 0;
     113                 :            : 
     114                 :        176 :         return ops->store(kobj, of->kn->priv, buf, count);
     115                 :            : }
     116                 :            : 
     117                 :            : /* kernfs write callback for bin sysfs files */
     118                 :          0 : static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf,
     119                 :            :                                   size_t count, loff_t pos)
     120                 :            : {
     121                 :          0 :         struct bin_attribute *battr = of->kn->priv;
     122                 :          0 :         struct kobject *kobj = of->kn->parent->priv;
     123                 :          0 :         loff_t size = file_inode(of->file)->i_size;
     124                 :            : 
     125         [ #  # ]:          0 :         if (size) {
     126         [ #  # ]:          0 :                 if (size <= pos)
     127                 :            :                         return 0;
     128                 :          0 :                 count = min_t(ssize_t, count, size - pos);
     129                 :            :         }
     130         [ #  # ]:          0 :         if (!count)
     131                 :            :                 return 0;
     132                 :            : 
     133         [ #  # ]:          0 :         if (!battr->write)
     134                 :            :                 return -EIO;
     135                 :            : 
     136                 :          0 :         return battr->write(of->file, kobj, battr, buf, pos, count);
     137                 :            : }
     138                 :            : 
     139                 :          0 : static int sysfs_kf_bin_mmap(struct kernfs_open_file *of,
     140                 :            :                              struct vm_area_struct *vma)
     141                 :            : {
     142                 :          0 :         struct bin_attribute *battr = of->kn->priv;
     143                 :          0 :         struct kobject *kobj = of->kn->parent->priv;
     144                 :            : 
     145                 :          0 :         return battr->mmap(of->file, kobj, battr, vma);
     146                 :            : }
     147                 :            : 
     148                 :          0 : void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr)
     149                 :            : {
     150                 :          0 :         struct kernfs_node *kn = kobj->sd, *tmp;
     151                 :            : 
     152         [ #  # ]:          0 :         if (kn && dir)
     153                 :            :                 kn = kernfs_find_and_get(kn, dir);
     154                 :            :         else
     155                 :          0 :                 kernfs_get(kn);
     156                 :            : 
     157         [ #  # ]:          0 :         if (kn && attr) {
     158                 :            :                 tmp = kernfs_find_and_get(kn, attr);
     159                 :          0 :                 kernfs_put(kn);
     160                 :            :                 kn = tmp;
     161                 :            :         }
     162                 :            : 
     163         [ #  # ]:          0 :         if (kn) {
     164                 :          0 :                 kernfs_notify(kn);
     165                 :          0 :                 kernfs_put(kn);
     166                 :            :         }
     167                 :          0 : }
     168                 :            : EXPORT_SYMBOL_GPL(sysfs_notify);
     169                 :            : 
     170                 :            : static const struct kernfs_ops sysfs_file_kfops_empty = {
     171                 :            : };
     172                 :            : 
     173                 :            : static const struct kernfs_ops sysfs_file_kfops_ro = {
     174                 :            :         .seq_show       = sysfs_kf_seq_show,
     175                 :            : };
     176                 :            : 
     177                 :            : static const struct kernfs_ops sysfs_file_kfops_wo = {
     178                 :            :         .write          = sysfs_kf_write,
     179                 :            : };
     180                 :            : 
     181                 :            : static const struct kernfs_ops sysfs_file_kfops_rw = {
     182                 :            :         .seq_show       = sysfs_kf_seq_show,
     183                 :            :         .write          = sysfs_kf_write,
     184                 :            : };
     185                 :            : 
     186                 :            : static const struct kernfs_ops sysfs_bin_kfops_ro = {
     187                 :            :         .read           = sysfs_kf_bin_read,
     188                 :            : };
     189                 :            : 
     190                 :            : static const struct kernfs_ops sysfs_bin_kfops_wo = {
     191                 :            :         .write          = sysfs_kf_bin_write,
     192                 :            : };
     193                 :            : 
     194                 :            : static const struct kernfs_ops sysfs_bin_kfops_rw = {
     195                 :            :         .read           = sysfs_kf_bin_read,
     196                 :            :         .write          = sysfs_kf_bin_write,
     197                 :            : };
     198                 :            : 
     199                 :            : static const struct kernfs_ops sysfs_bin_kfops_mmap = {
     200                 :            :         .read           = sysfs_kf_bin_read,
     201                 :            :         .write          = sysfs_kf_bin_write,
     202                 :            :         .mmap           = sysfs_kf_bin_mmap,
     203                 :            : };
     204                 :            : 
     205                 :          0 : int sysfs_add_file_mode_ns(struct kernfs_node *parent,
     206                 :            :                            const struct attribute *attr, bool is_bin,
     207                 :            :                            umode_t mode, const void *ns)
     208                 :            : {
     209                 :            :         struct lock_class_key *key = NULL;
     210                 :            :         const struct kernfs_ops *ops;
     211                 :            :         struct kernfs_node *kn;
     212                 :            :         loff_t size;
     213                 :            : 
     214         [ +  + ]:        966 :         if (!is_bin) {
     215                 :        962 :                 struct kobject *kobj = parent->priv;
     216                 :        962 :                 const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
     217                 :            : 
     218                 :            :                 /* every kobject with an attribute needs a ktype assigned */
     219 [ -  + ][ +  - ]:        962 :                 if (WARN(!sysfs_ops, KERN_ERR
     220                 :            :                          "missing sysfs attribute operations for kobject: %s\n",
     221                 :            :                          kobject_name(kobj)))
     222                 :            :                         return -EINVAL;
     223                 :            : 
     224 [ +  - ][ -  + ]:        962 :                 if (sysfs_ops->show && sysfs_ops->store)
     225                 :            :                         ops = &sysfs_file_kfops_rw;
     226         [ #  # ]:          0 :                 else if (sysfs_ops->show)
     227                 :            :                         ops = &sysfs_file_kfops_ro;
     228         [ #  # ]:          0 :                 else if (sysfs_ops->store)
     229                 :            :                         ops = &sysfs_file_kfops_wo;
     230                 :            :                 else
     231                 :            :                         ops = &sysfs_file_kfops_empty;
     232                 :            : 
     233                 :            :                 size = PAGE_SIZE;
     234                 :            :         } else {
     235                 :            :                 struct bin_attribute *battr = (void *)attr;
     236                 :            : 
     237         [ +  - ]:          4 :                 if (battr->mmap)
     238                 :            :                         ops = &sysfs_bin_kfops_mmap;
     239 [ +  - ][ +  - ]:          4 :                 else if (battr->read && battr->write)
     240                 :            :                         ops = &sysfs_bin_kfops_rw;
     241         [ -  + ]:          4 :                 else if (battr->read)
     242                 :            :                         ops = &sysfs_bin_kfops_ro;
     243         [ #  # ]:          0 :                 else if (battr->write)
     244                 :            :                         ops = &sysfs_bin_kfops_wo;
     245                 :            :                 else
     246                 :            :                         ops = &sysfs_file_kfops_empty;
     247                 :            : 
     248                 :          4 :                 size = battr->size;
     249                 :            :         }
     250                 :            : 
     251                 :            : #ifdef CONFIG_DEBUG_LOCK_ALLOC
     252                 :            :         if (!attr->ignore_lockdep)
     253                 :            :                 key = attr->key ?: (struct lock_class_key *)&attr->skey;
     254                 :            : #endif
     255                 :        966 :         kn = __kernfs_create_file(parent, attr->name, mode, size, ops,
     256                 :            :                                   (void *)attr, ns, true, key);
     257         [ -  + ]:        966 :         if (IS_ERR(kn)) {
     258         [ #  # ]:          0 :                 if (PTR_ERR(kn) == -EEXIST)
     259                 :          0 :                         sysfs_warn_dup(parent, attr->name);
     260                 :          0 :                 return PTR_ERR(kn);
     261                 :            :         }
     262                 :            :         return 0;
     263                 :            : }
     264                 :            : 
     265                 :          0 : int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr,
     266                 :            :                    bool is_bin)
     267                 :            : {
     268                 :         24 :         return sysfs_add_file_mode_ns(parent, attr, is_bin, attr->mode, NULL);
     269                 :            : }
     270                 :            : 
     271                 :            : /**
     272                 :            :  * sysfs_create_file_ns - create an attribute file for an object with custom ns
     273                 :            :  * @kobj: object we're creating for
     274                 :            :  * @attr: attribute descriptor
     275                 :            :  * @ns: namespace the new file should belong to
     276                 :            :  */
     277                 :          0 : int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
     278                 :            :                          const void *ns)
     279                 :            : {
     280 [ +  - ][ +  - ]:        288 :         BUG_ON(!kobj || !kobj->sd || !attr);
         [ +  - ][ -  + ]
     281                 :            : 
     282                 :        288 :         return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns);
     283                 :            : 
     284                 :            : }
     285                 :            : EXPORT_SYMBOL_GPL(sysfs_create_file_ns);
     286                 :            : 
     287                 :          0 : int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
     288                 :            : {
     289                 :            :         int err = 0;
     290                 :            :         int i;
     291                 :            : 
     292 [ #  # ][ #  # ]:          0 :         for (i = 0; ptr[i] && !err; i++)
     293                 :            :                 err = sysfs_create_file(kobj, ptr[i]);
     294         [ #  # ]:          0 :         if (err)
     295         [ #  # ]:          0 :                 while (--i >= 0)
     296                 :          0 :                         sysfs_remove_file(kobj, ptr[i]);
     297                 :          0 :         return err;
     298                 :            : }
     299                 :            : EXPORT_SYMBOL_GPL(sysfs_create_files);
     300                 :            : 
     301                 :            : /**
     302                 :            :  * sysfs_add_file_to_group - add an attribute file to a pre-existing group.
     303                 :            :  * @kobj: object we're acting for.
     304                 :            :  * @attr: attribute descriptor.
     305                 :            :  * @group: group name.
     306                 :            :  */
     307                 :          0 : int sysfs_add_file_to_group(struct kobject *kobj,
     308                 :            :                 const struct attribute *attr, const char *group)
     309                 :            : {
     310                 :            :         struct kernfs_node *parent;
     311                 :            :         int error;
     312                 :            : 
     313         [ +  - ]:          2 :         if (group) {
     314                 :          2 :                 parent = kernfs_find_and_get(kobj->sd, group);
     315                 :            :         } else {
     316                 :          0 :                 parent = kobj->sd;
     317                 :          0 :                 kernfs_get(parent);
     318                 :            :         }
     319                 :            : 
     320         [ +  - ]:          2 :         if (!parent)
     321                 :            :                 return -ENOENT;
     322                 :            : 
     323                 :            :         error = sysfs_add_file(parent, attr, false);
     324                 :          2 :         kernfs_put(parent);
     325                 :            : 
     326                 :          2 :         return error;
     327                 :            : }
     328                 :            : EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);
     329                 :            : 
     330                 :            : /**
     331                 :            :  * sysfs_chmod_file - update the modified mode value on an object attribute.
     332                 :            :  * @kobj: object we're acting for.
     333                 :            :  * @attr: attribute descriptor.
     334                 :            :  * @mode: file permissions.
     335                 :            :  *
     336                 :            :  */
     337                 :          0 : int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr,
     338                 :            :                      umode_t mode)
     339                 :            : {
     340                 :            :         struct kernfs_node *kn;
     341                 :            :         struct iattr newattrs;
     342                 :            :         int rc;
     343                 :            : 
     344                 :          0 :         kn = kernfs_find_and_get(kobj->sd, attr->name);
     345         [ #  # ]:          0 :         if (!kn)
     346                 :            :                 return -ENOENT;
     347                 :            : 
     348                 :          0 :         newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO);
     349                 :          0 :         newattrs.ia_valid = ATTR_MODE;
     350                 :            : 
     351                 :          0 :         rc = kernfs_setattr(kn, &newattrs);
     352                 :            : 
     353                 :          0 :         kernfs_put(kn);
     354                 :          0 :         return rc;
     355                 :            : }
     356                 :            : EXPORT_SYMBOL_GPL(sysfs_chmod_file);
     357                 :            : 
     358                 :            : /**
     359                 :            :  * sysfs_remove_file_ns - remove an object attribute with a custom ns tag
     360                 :            :  * @kobj: object we're acting for
     361                 :            :  * @attr: attribute descriptor
     362                 :            :  * @ns: namespace tag of the file to remove
     363                 :            :  *
     364                 :            :  * Hash the attribute name and namespace tag and kill the victim.
     365                 :            :  */
     366                 :          0 : void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr,
     367                 :            :                           const void *ns)
     368                 :            : {
     369                 :         40 :         struct kernfs_node *parent = kobj->sd;
     370                 :            : 
     371                 :         40 :         kernfs_remove_by_name_ns(parent, attr->name, ns);
     372                 :         40 : }
     373                 :            : EXPORT_SYMBOL_GPL(sysfs_remove_file_ns);
     374                 :            : 
     375                 :          0 : void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
     376                 :            : {
     377                 :            :         int i;
     378         [ #  # ]:          0 :         for (i = 0; ptr[i]; i++)
     379                 :            :                 sysfs_remove_file(kobj, ptr[i]);
     380                 :          0 : }
     381                 :            : EXPORT_SYMBOL_GPL(sysfs_remove_files);
     382                 :            : 
     383                 :            : /**
     384                 :            :  * sysfs_remove_file_from_group - remove an attribute file from a group.
     385                 :            :  * @kobj: object we're acting for.
     386                 :            :  * @attr: attribute descriptor.
     387                 :            :  * @group: group name.
     388                 :            :  */
     389                 :          0 : void sysfs_remove_file_from_group(struct kobject *kobj,
     390                 :            :                 const struct attribute *attr, const char *group)
     391                 :            : {
     392                 :            :         struct kernfs_node *parent;
     393                 :            : 
     394         [ +  - ]:          2 :         if (group) {
     395                 :          2 :                 parent = kernfs_find_and_get(kobj->sd, group);
     396                 :            :         } else {
     397                 :          0 :                 parent = kobj->sd;
     398                 :          0 :                 kernfs_get(parent);
     399                 :            :         }
     400                 :            : 
     401         [ +  - ]:          2 :         if (parent) {
     402                 :          2 :                 kernfs_remove_by_name(parent, attr->name);
     403                 :          2 :                 kernfs_put(parent);
     404                 :            :         }
     405                 :          2 : }
     406                 :            : EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);
     407                 :            : 
     408                 :            : /**
     409                 :            :  *      sysfs_create_bin_file - create binary file for object.
     410                 :            :  *      @kobj:  object.
     411                 :            :  *      @attr:  attribute descriptor.
     412                 :            :  */
     413                 :          0 : int sysfs_create_bin_file(struct kobject *kobj,
     414                 :            :                           const struct bin_attribute *attr)
     415                 :            : {
     416 [ +  - ][ +  - ]:          4 :         BUG_ON(!kobj || !kobj->sd || !attr);
         [ +  - ][ -  + ]
     417                 :            : 
     418                 :          8 :         return sysfs_add_file(kobj->sd, &attr->attr, true);
     419                 :            : }
     420                 :            : EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
     421                 :            : 
     422                 :            : /**
     423                 :            :  *      sysfs_remove_bin_file - remove binary file for object.
     424                 :            :  *      @kobj:  object.
     425                 :            :  *      @attr:  attribute descriptor.
     426                 :            :  */
     427                 :          0 : void sysfs_remove_bin_file(struct kobject *kobj,
     428                 :            :                            const struct bin_attribute *attr)
     429                 :            : {
     430                 :          4 :         kernfs_remove_by_name(kobj->sd, attr->attr.name);
     431                 :          4 : }
     432                 :            : EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);
     433                 :            : 
     434                 :            : struct sysfs_schedule_callback_struct {
     435                 :            :         struct list_head        workq_list;
     436                 :            :         struct kobject          *kobj;
     437                 :            :         void                    (*func)(void *);
     438                 :            :         void                    *data;
     439                 :            :         struct module           *owner;
     440                 :            :         struct work_struct      work;
     441                 :            : };
     442                 :            : 
     443                 :            : static struct workqueue_struct *sysfs_workqueue;
     444                 :            : static DEFINE_MUTEX(sysfs_workq_mutex);
     445                 :            : static LIST_HEAD(sysfs_workq);
     446                 :          0 : static void sysfs_schedule_callback_work(struct work_struct *work)
     447                 :            : {
     448                 :          0 :         struct sysfs_schedule_callback_struct *ss = container_of(work,
     449                 :            :                         struct sysfs_schedule_callback_struct, work);
     450                 :            : 
     451                 :          0 :         (ss->func)(ss->data);
     452                 :          0 :         kobject_put(ss->kobj);
     453                 :          0 :         module_put(ss->owner);
     454                 :          0 :         mutex_lock(&sysfs_workq_mutex);
     455                 :            :         list_del(&ss->workq_list);
     456                 :          0 :         mutex_unlock(&sysfs_workq_mutex);
     457                 :          0 :         kfree(ss);
     458                 :          0 : }
     459                 :            : 
     460                 :            : /**
     461                 :            :  * sysfs_schedule_callback - helper to schedule a callback for a kobject
     462                 :            :  * @kobj: object we're acting for.
     463                 :            :  * @func: callback function to invoke later.
     464                 :            :  * @data: argument to pass to @func.
     465                 :            :  * @owner: module owning the callback code
     466                 :            :  *
     467                 :            :  * sysfs attribute methods must not unregister themselves or their parent
     468                 :            :  * kobject (which would amount to the same thing).  Attempts to do so will
     469                 :            :  * deadlock, since unregistration is mutually exclusive with driver
     470                 :            :  * callbacks.
     471                 :            :  *
     472                 :            :  * Instead methods can call this routine, which will attempt to allocate
     473                 :            :  * and schedule a workqueue request to call back @func with @data as its
     474                 :            :  * argument in the workqueue's process context.  @kobj will be pinned
     475                 :            :  * until @func returns.
     476                 :            :  *
     477                 :            :  * Returns 0 if the request was submitted, -ENOMEM if storage could not
     478                 :            :  * be allocated, -ENODEV if a reference to @owner isn't available,
     479                 :            :  * -EAGAIN if a callback has already been scheduled for @kobj.
     480                 :            :  */
     481                 :          0 : int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
     482                 :            :                 void *data, struct module *owner)
     483                 :            : {
     484                 :            :         struct sysfs_schedule_callback_struct *ss, *tmp;
     485                 :            : 
     486         [ #  # ]:          0 :         if (!try_module_get(owner))
     487                 :            :                 return -ENODEV;
     488                 :            : 
     489                 :          0 :         mutex_lock(&sysfs_workq_mutex);
     490         [ #  # ]:          0 :         list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list)
     491         [ #  # ]:          0 :                 if (ss->kobj == kobj) {
     492                 :          0 :                         module_put(owner);
     493                 :          0 :                         mutex_unlock(&sysfs_workq_mutex);
     494                 :          0 :                         return -EAGAIN;
     495                 :            :                 }
     496                 :          0 :         mutex_unlock(&sysfs_workq_mutex);
     497                 :            : 
     498         [ #  # ]:          0 :         if (sysfs_workqueue == NULL) {
     499                 :          0 :                 sysfs_workqueue = create_singlethread_workqueue("sysfsd");
     500         [ #  # ]:          0 :                 if (sysfs_workqueue == NULL) {
     501                 :          0 :                         module_put(owner);
     502                 :          0 :                         return -ENOMEM;
     503                 :            :                 }
     504                 :            :         }
     505                 :            : 
     506                 :            :         ss = kmalloc(sizeof(*ss), GFP_KERNEL);
     507         [ #  # ]:          0 :         if (!ss) {
     508                 :          0 :                 module_put(owner);
     509                 :          0 :                 return -ENOMEM;
     510                 :            :         }
     511                 :          0 :         kobject_get(kobj);
     512                 :          0 :         ss->kobj = kobj;
     513                 :          0 :         ss->func = func;
     514                 :          0 :         ss->data = data;
     515                 :          0 :         ss->owner = owner;
     516                 :          0 :         INIT_WORK(&ss->work, sysfs_schedule_callback_work);
     517                 :          0 :         INIT_LIST_HEAD(&ss->workq_list);
     518                 :          0 :         mutex_lock(&sysfs_workq_mutex);
     519                 :            :         list_add_tail(&ss->workq_list, &sysfs_workq);
     520                 :          0 :         mutex_unlock(&sysfs_workq_mutex);
     521                 :          0 :         queue_work(sysfs_workqueue, &ss->work);
     522                 :          0 :         return 0;
     523                 :            : }
     524                 :            : EXPORT_SYMBOL_GPL(sysfs_schedule_callback);

Generated by: LCOV version 1.9