- Updated to 3.3-rc5.
authorJeff Mahoney <jeffm@suse.com>
Mon, 27 Feb 2012 02:38:40 +0000 (21:38 -0500)
committerJeff Mahoney <jeffm@suse.com>
Mon, 27 Feb 2012 02:38:40 +0000 (21:38 -0500)
suse-commit: 0b7f0483532db81fec95961d9177362cac488a1f

1  2 
Makefile
drivers/block/nvme.c
drivers/scsi/scsi_scan.c
fs/namei.c
fs/super.c
include/linux/fs.h
mm/page_alloc.c
scripts/mod/modpost.c

diff --combined Makefile
+++ b/Makefile
@@@ -1,7 -1,7 +1,7 @@@
  VERSION = 3
  PATCHLEVEL = 3
  SUBLEVEL = 0
- EXTRAVERSION = -rc4
+ EXTRAVERSION = -rc5
  NAME = Saber-toothed Squirrel
  
  # *DOCUMENTATION*
@@@ -65,20 -65,6 +65,20 @@@ ifndef KBUILD_CHECKSR
    KBUILD_CHECKSRC = 0
  endif
  
 +# Call message checker as part of the C compilation
 +#
 +# Use 'make D=1' to enable checking
 +# Use 'make D=2' to create the message catalog
 +
 +ifdef D
 +  ifeq ("$(origin D)", "command line")
 +    KBUILD_KMSG_CHECK = $(D)
 +  endif
 +endif
 +ifndef KBUILD_KMSG_CHECK
 +  KBUILD_KMSG_CHECK = 0
 +endif
 +
  # Use make M=dir to specify directory of external module to build
  # Old syntax make ... SUBDIRS=$PWD is still supported
  # Setting the environment variable KBUILD_EXTMOD take precedence
@@@ -361,7 -347,6 +361,7 @@@ CHECK              = spars
  
  CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
                  -Wbitwise -Wno-return-void $(CF)
 +KMSG_CHECK    = $(srctree)/scripts/kmsg-doc
  CFLAGS_MODULE   =
  AFLAGS_MODULE   =
  LDFLAGS_MODULE  =
@@@ -391,11 -376,6 +391,11 @@@ KBUILD_AFLAGS_MODULE  := -DMODUL
  KBUILD_CFLAGS_MODULE  := -DMODULE
  KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
  
 +# Warn about unsupported modules in kernels built inside Autobuild
 +ifneq ($(wildcard /.buildenv),)
 +CFLAGS                += -DUNSUPPORTED_MODULES=2
 +endif
 +
  # Read KERNELRELEASE from include/config/kernel.release (if it exists)
  KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
  KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
@@@ -412,7 -392,6 +412,7 @@@ export KBUILD_AFLAGS AFLAGS_KERNEL AFLA
  export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
  export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
  export KBUILD_ARFLAGS
 +export KBUILD_KMSG_CHECK KMSG_CHECK
  
  # When compiling out-of-tree modules, put MODVERDIR in the module
  # tree rather than in the kernel tree. The kernel tree might
@@@ -613,11 -592,6 +613,11 @@@ KBUILD_CFLAGS    += -fomit-frame-pointe
  endif
  endif
  
 +ifdef CONFIG_UNWIND_INFO
 +KBUILD_CFLAGS += -fasynchronous-unwind-tables
 +LDFLAGS_vmlinux       += --eh-frame-hdr
 +endif
 +
  ifdef CONFIG_DEBUG_INFO
  KBUILD_CFLAGS += -g
  KBUILD_AFLAGS += -gdwarf-2
@@@ -1053,7 -1027,7 +1053,7 @@@ depend dep
  
  # ---------------------------------------------------------------------------
  # Firmware install
 -INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware
 +INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware/$(KERNELRELEASE)
  export INSTALL_FW_PATH
  
  PHONY += firmware_install
diff --combined drivers/block/nvme.c
@@@ -41,6 -41,8 +41,8 @@@
  #include <linux/types.h>
  #include <linux/version.h>
  
+ #include <asm-generic/io-64-nonatomic-lo-hi.h>
  #define NVME_Q_DEPTH 1024
  #define SQ_SIZE(depth)                (depth * sizeof(struct nvme_command))
  #define CQ_SIZE(depth)                (depth * sizeof(struct nvme_completion))
@@@ -996,7 -998,6 +998,7 @@@ static int __devinit nvme_configure_adm
  
        writel(0, &dev->bar->cc);
        writel(aqa, &dev->bar->aqa);
 +      /* These are not defined on all archs */
        writeq(nvmeq->sq_dma_addr, &dev->bar->asq);
        writeq(nvmeq->cq_dma_addr, &dev->bar->acq);
        writel(dev->ctrl_config, &dev->bar->cc);
diff --combined drivers/scsi/scsi_scan.c
@@@ -693,7 -693,7 +693,7 @@@ static int scsi_probe_lun(struct scsi_d
         * and displaying garbage for the Vendor, Product, or Revision
         * strings.
         */
 -      if (sdev->inquiry_len < 36) {
 +      if (sdev->inquiry_len < 36 && printk_ratelimit()) {
                printk(KERN_INFO "scsi scan: INQUIRY result too short (%d),"
                                " using 36\n", sdev->inquiry_len);
                sdev->inquiry_len = 36;
@@@ -1815,6 -1815,7 +1815,7 @@@ static void scsi_finish_async_scan(stru
        }
        spin_unlock(&async_scan_lock);
  
+       scsi_autopm_put_host(shost);
        scsi_host_put(shost);
        kfree(data);
  }
@@@ -1841,7 -1842,6 +1842,6 @@@ static int do_scan_async(void *_data
  
        do_scsi_scan_host(shost);
        scsi_finish_async_scan(data);
-       scsi_autopm_put_host(shost);
        return 0;
  }
  
@@@ -1869,7 -1869,7 +1869,7 @@@ void scsi_scan_host(struct Scsi_Host *s
        p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
        if (IS_ERR(p))
                do_scan_async(data);
-       /* scsi_autopm_put_host(shost) is called in do_scan_async() */
+       /* scsi_autopm_put_host(shost) is called in scsi_finish_async_scan() */
  }
  EXPORT_SYMBOL(scsi_scan_host);
  
diff --combined fs/namei.c
@@@ -1095,8 -1095,10 +1095,10 @@@ static struct dentry *d_inode_lookup(st
        struct dentry *old;
  
        /* Don't create child dentry for a dead directory. */
-       if (unlikely(IS_DEADDIR(inode)))
+       if (unlikely(IS_DEADDIR(inode))) {
+               dput(dentry);
                return ERR_PTR(-ENOENT);
+       }
  
        old = inode->i_op->lookup(inode, dentry, nd);
        if (unlikely(old)) {
@@@ -1868,26 -1870,6 +1870,26 @@@ other_userns
  }
  
  /*
 + * Do the directory specific tests of inode_permission() and call the
 + * may_delete inode operation.  The may_delete inode operation must do the
 + * sticky check when needed.
 + */
 +static int may_delete_iop(struct inode *dir, struct inode *inode, int replace)
 +{
 +      int error;
 +
 +      if (IS_RDONLY(dir))
 +              return -EROFS;
 +      if (IS_IMMUTABLE(dir))
 +              return -EACCES;
 +      error = dir->i_op->may_delete(dir, inode, replace);
 +      if (!error)
 +              error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC);
 +
 +      return error;
 +}
 +
 +/*
   *    Check whether we can remove a link victim from directory dir, check
   *  whether the type of victim is right.
   *  1. We can't do it if dir is read-only (done in permission())
   * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
   *     nfs_async_unlink().
   */
 -static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
 +static int may_delete(struct inode *dir, struct dentry *victim,
 +                    int isdir, int replace)
  {
        int error;
  
  
        BUG_ON(victim->d_parent->d_inode != dir);
        audit_inode_child(victim, dir);
 -
 -      error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
 +      if (dir->i_op->may_delete)
 +              error = may_delete_iop(dir, victim->d_inode, replace);
 +      else {
 +              error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
 +              if (!error && check_sticky(dir, victim->d_inode))
 +                      error = -EPERM;
 +      }
        if (error)
                return error;
        if (IS_APPEND(dir))
                return -EPERM;
 -      if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
 -          IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
 +      if (IS_APPEND(victim->d_inode) || IS_IMMUTABLE(victim->d_inode) ||
 +              IS_SWAPFILE(victim->d_inode))
                return -EPERM;
        if (isdir) {
                if (!S_ISDIR(victim->d_inode->i_mode))
        return 0;
  }
  
 +/*
 + * Do the directory specific tests of inode_permission() and call the
 + * may_create inode operation.
 + */
 +static int may_create_iop(struct inode *dir, int isdir)
 +{
 +      int error;
 +
 +      if (IS_RDONLY(dir))
 +              return -EROFS;
 +      if (IS_IMMUTABLE(dir))
 +              return -EACCES;
 +      error = dir->i_op->may_create(dir, isdir);
 +      if (!error)
 +              error = security_inode_permission(dir, MAY_WRITE | MAY_EXEC);
 +
 +      return error;
 +}
 +
  /*    Check whether we can create an object with dentry child in directory
   *  dir.
   *  1. We can't do it if child already exists (open has special treatment for
   *  3. We should have write and exec permissions on dir
   *  4. We can't do it if dir is immutable (done in permission())
   */
 -static inline int may_create(struct inode *dir, struct dentry *child)
 +static inline int may_create(struct inode *dir, struct dentry *child, int isdir)
  {
        if (child->d_inode)
                return -EEXIST;
        if (IS_DEADDIR(dir))
                return -ENOENT;
 -      return inode_permission(dir, MAY_WRITE | MAY_EXEC);
 +      if (dir->i_op->may_create)
 +              return may_create_iop(dir, isdir);
 +      else
 +              return inode_permission(dir, MAY_WRITE | MAY_EXEC);
  }
  
  /*
@@@ -2028,7 -1982,7 +2030,7 @@@ void unlock_rename(struct dentry *p1, s
  int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                struct nameidata *nd)
  {
 -      int error = may_create(dir, dentry);
 +      int error = may_create(dir, dentry, 0);
  
        if (error)
                return error;
@@@ -2227,7 -2181,7 +2229,7 @@@ static struct file *do_last(struct name
        /* Negative dentry, just create the file */
        if (!dentry->d_inode) {
                umode_t mode = op->mode;
 -              if (!IS_POSIXACL(dir->d_inode))
 +              if (!IS_ACL(dir->d_inode))
                        mode &= ~current_umask();
                /*
                 * This write is needed to ensure that a
@@@ -2495,7 -2449,7 +2497,7 @@@ EXPORT_SYMBOL(user_path_create)
  
  int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
  {
 -      int error = may_create(dir, dentry);
 +      int error = may_create(dir, dentry, 0);
  
        if (error)
                return error;
@@@ -2552,7 -2506,7 +2554,7 @@@ SYSCALL_DEFINE4(mknodat, int, dfd, cons
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
  
 -      if (!IS_POSIXACL(path.dentry->d_inode))
 +      if (!IS_ACL(path.dentry->d_inode))
                mode &= ~current_umask();
        error = may_mknod(mode);
        if (error)
@@@ -2592,7 -2546,7 +2594,7 @@@ SYSCALL_DEFINE3(mknod, const char __use
  
  int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
  {
 -      int error = may_create(dir, dentry);
 +      int error = may_create(dir, dentry, 1);
  
        if (error)
                return error;
@@@ -2621,7 -2575,7 +2623,7 @@@ SYSCALL_DEFINE3(mkdirat, int, dfd, cons
        if (IS_ERR(dentry))
                return PTR_ERR(dentry);
  
 -      if (!IS_POSIXACL(path.dentry->d_inode))
 +      if (!IS_ACL(path.dentry->d_inode))
                mode &= ~current_umask();
        error = mnt_want_write(path.mnt);
        if (error)
@@@ -2670,7 -2624,7 +2672,7 @@@ void dentry_unhash(struct dentry *dentr
  
  int vfs_rmdir(struct inode *dir, struct dentry *dentry)
  {
 -      int error = may_delete(dir, dentry, 1);
 +      int error = may_delete(dir, dentry, 1, 0);
  
        if (error)
                return error;
@@@ -2765,7 -2719,7 +2767,7 @@@ SYSCALL_DEFINE1(rmdir, const char __use
  
  int vfs_unlink(struct inode *dir, struct dentry *dentry)
  {
 -      int error = may_delete(dir, dentry, 0);
 +      int error = may_delete(dir, dentry, 0, 0);
  
        if (error)
                return error;
@@@ -2874,7 -2828,7 +2876,7 @@@ SYSCALL_DEFINE1(unlink, const char __us
  
  int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
  {
 -      int error = may_create(dir, dentry);
 +      int error = may_create(dir, dentry, 0);
  
        if (error)
                return error;
@@@ -2940,7 -2894,7 +2942,7 @@@ int vfs_link(struct dentry *old_dentry
        if (!inode)
                return -ENOENT;
  
 -      error = may_create(dir, new_dentry);
 +      error = may_create(dir, new_dentry, S_ISDIR(inode->i_mode));
        if (error)
                return error;
  
@@@ -3160,14 -3114,14 +3162,14 @@@ int vfs_rename(struct inode *old_dir, s
        if (old_dentry->d_inode == new_dentry->d_inode)
                return 0;
   
 -      error = may_delete(old_dir, old_dentry, is_dir);
 +      error = may_delete(old_dir, old_dentry, is_dir, 0);
        if (error)
                return error;
  
        if (!new_dentry->d_inode)
 -              error = may_create(new_dir, new_dentry);
 +              error = may_create(new_dir, new_dentry, is_dir);
        else
 -              error = may_delete(new_dir, new_dentry, is_dir);
 +              error = may_delete(new_dir, new_dentry, is_dir, 1);
        if (error)
                return error;
  
diff --combined fs/super.c
@@@ -634,6 -634,28 +634,28 @@@ rescan
  EXPORT_SYMBOL(get_super);
  
  /**
+  *    get_super_thawed - get thawed superblock of a device
+  *    @bdev: device to get the superblock for
+  *
+  *    Scans the superblock list and finds the superblock of the file system
+  *    mounted on the device. The superblock is returned once it is thawed
+  *    (or immediately if it was not frozen). %NULL is returned if no match
+  *    is found.
+  */
+ struct super_block *get_super_thawed(struct block_device *bdev)
+ {
+       while (1) {
+               struct super_block *s = get_super(bdev);
+               if (!s || s->s_frozen == SB_UNFROZEN)
+                       return s;
+               up_read(&s->s_umount);
+               vfs_check_frozen(s, SB_FREEZE_WRITE);
+               put_super(s);
+       }
+ }
+ EXPORT_SYMBOL(get_super_thawed);
+ /**
   * get_active_super - get an active reference to the superblock of a device
   * @bdev: device to get the superblock for
   *
@@@ -691,10 -713,16 +713,10 @@@ rescan
        return NULL;
  }
  
 -/**
 - *    do_remount_sb - asks filesystem to change mount options.
 - *    @sb:    superblock in question
 - *    @flags: numeric part of options
 - *    @data:  the rest of options
 - *      @force: whether or not to force the change
 - *
 - *    Alters the mount options of a mounted file system.
 - */
 -int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 +#define REMOUNT_FORCE         1
 +#define REMOUNT_SHRINK_DCACHE 2
 +
 +static int __do_remount_sb(struct super_block *sb, int flags, void *data, int rflags)
  {
        int retval;
        int remount_ro;
  
        if (flags & MS_RDONLY)
                acct_auto_close(sb);
 -      shrink_dcache_sb(sb);
 +      if (rflags & REMOUNT_SHRINK_DCACHE)
 +              shrink_dcache_sb(sb);
        sync_filesystem(sb);
  
        remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY);
        /* If we are remounting RDONLY and current sb is read/write,
           make sure there are no rw files opened */
        if (remount_ro) {
 -              if (force) {
 +              if (rflags & REMOUNT_FORCE) {
                        mark_files_ro(sb);
                } else {
                        retval = sb_prepare_remount_readonly(sb);
        if (sb->s_op->remount_fs) {
                retval = sb->s_op->remount_fs(sb, &flags, data);
                if (retval) {
 -                      if (!force)
 +                      if (!(rflags & REMOUNT_FORCE))
                                goto cancel_readonly;
                        /* If forced remount, go ahead despite any errors */
                        WARN(1, "forced remount of a %s fs returned %i\n",
@@@ -759,21 -786,6 +781,21 @@@ cancel_readonly
        return retval;
  }
  
 +/**
 + *    do_remount_sb - asks filesystem to change mount options.
 + *    @sb:    superblock in question
 + *    @flags: numeric part of options
 + *    @data:  the rest of options
 + *      @force: whether or not to force the change
 + *
 + *    Alters the mount options of a mounted file system.
 + */
 +int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 +{
 +      return __do_remount_sb(sb, flags, data,
 +                      REMOUNT_SHRINK_DCACHE|(force? REMOUNT_FORCE : 0));
 +}
 +
  static void do_emergency_remount(struct work_struct *work)
  {
        struct super_block *sb, *p = NULL;
@@@ -1090,7 -1102,7 +1112,7 @@@ struct dentry *mount_single(struct file
                }
                s->s_flags |= MS_ACTIVE;
        } else {
 -              do_remount_sb(s, flags, data, 0);
 +              __do_remount_sb(s, flags, data, 0);
        }
        return dget(s->s_root);
  }
diff --combined include/linux/fs.h
@@@ -201,7 -201,7 +201,7 @@@ struct inodes_stat_t 
  #define MS_VERBOSE    32768   /* War is peace. Verbosity is silence.
                                   MS_VERBOSE is deprecated. */
  #define MS_SILENT     32768
 -#define MS_POSIXACL   (1<<16) /* VFS does not apply the umask */
 +#define MS_POSIXACL   (1<<16) /* Supports POSIX ACLs */
  #define MS_UNBINDABLE (1<<17) /* change to unbindable */
  #define MS_PRIVATE    (1<<18) /* change to private */
  #define MS_SLAVE      (1<<19) /* change to slave */
  #define MS_KERNMOUNT  (1<<22) /* this is a kern_mount call */
  #define MS_I_VERSION  (1<<23) /* Update inode I_version field */
  #define MS_STRICTATIME        (1<<24) /* Always perform atime updates */
 +#define MS_RICHACL    (1<<25) /* Supports richacls */
  #define MS_NOSEC      (1<<28)
  #define MS_BORN               (1<<29)
  #define MS_ACTIVE     (1<<30)
  #define IS_APPEND(inode)      ((inode)->i_flags & S_APPEND)
  #define IS_IMMUTABLE(inode)   ((inode)->i_flags & S_IMMUTABLE)
  #define IS_POSIXACL(inode)    __IS_FLG(inode, MS_POSIXACL)
 +#define IS_RICHACL(inode)     __IS_FLG(inode, MS_RICHACL)
  
  #define IS_DEADDIR(inode)     ((inode)->i_flags & S_DEAD)
  #define IS_NOCMTIME(inode)    ((inode)->i_flags & S_NOCMTIME)
  #define IS_AUTOMOUNT(inode)   ((inode)->i_flags & S_AUTOMOUNT)
  #define IS_NOSEC(inode)               ((inode)->i_flags & S_NOSEC)
  
 +/*
 + * IS_ACL() tells the VFS to not apply the umask
 + * and use iop->check_acl for acl permission checks when defined.
 + */
 +#define IS_ACL(inode)         __IS_FLG(inode, MS_POSIXACL | MS_RICHACL)
 +
  /* the read-only stuff doesn't really belong here, but any other place is
     probably as bad and I don't want to create yet another include file. */
  
@@@ -1661,10 -1653,6 +1661,10 @@@ struct inode_operations 
        void (*truncate_range)(struct inode *, loff_t, loff_t);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
                      u64 len);
 +      int (*may_create) (struct inode *, int);
 +      int (*may_delete) (struct inode *, struct inode *, int);
 +
 +
  } ____cacheline_aligned;
  
  struct seq_file;
@@@ -2508,6 -2496,7 +2508,7 @@@ extern void get_filesystem(struct file_
  extern void put_filesystem(struct file_system_type *fs);
  extern struct file_system_type *get_fs_type(const char *name);
  extern struct super_block *get_super(struct block_device *);
+ extern struct super_block *get_super_thawed(struct block_device *);
  extern struct super_block *get_active_super(struct block_device *bdev);
  extern void drop_super(struct super_block *sb);
  extern void iterate_supers(void (*)(struct super_block *, void *), void *);
diff --combined mm/page_alloc.c
@@@ -1874,13 -1874,7 +1874,13 @@@ void warn_alloc_failed(gfp_t gfp_mask, 
                va_end(args);
        }
  
 -      pr_warn("%s: page allocation failure: order:%d, mode:0x%x\n",
 +      if (!(gfp_mask & __GFP_WAIT)) {
 +              pr_info("The following is only an harmless informational message.\n");
 +              pr_info("Unless you get a _continuous_flood_ of these messages it means\n");
 +              pr_info("everything is working fine. Allocations from irqs cannot be\n");
 +              pr_info("perfectly reliable and the kernel is designed to handle that.\n");
 +      }
 +      pr_info("%s: page allocation failure. order:%d, mode:0x%x\n",
                current->comm, order, gfp_mask);
  
        dump_stack();
@@@ -5242,6 -5236,7 +5242,7 @@@ void *__init alloc_large_system_hash(co
                max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;
                do_div(max, bucketsize);
        }
+       max = min(max, 0x80000000ULL);
  
        if (numentries > max)
                numentries = max;
diff --combined scripts/mod/modpost.c
@@@ -1494,6 -1494,13 +1494,13 @@@ static int addend_386_rel(struct elf_in
        return 0;
  }
  
+ #ifndef R_ARM_CALL
+ #define R_ARM_CALL    28
+ #endif
+ #ifndef R_ARM_JUMP24
+ #define R_ARM_JUMP24  29
+ #endif
  static int addend_arm_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
  {
        unsigned int r_typ = ELF_R_TYPE(r->r_info);
                              (elf->symtab_start + ELF_R_SYM(r->r_info));
                break;
        case R_ARM_PC24:
+       case R_ARM_CALL:
+       case R_ARM_JUMP24:
                /* From ARM ABI: ((S + A) | T) - P */
                r->r_addend = (int)(long)(elf->hdr +
                              sechdr->sh_offset +
@@@ -1670,48 -1679,6 +1679,48 @@@ static void check_sec_ref(struct modul
        }
  }
  
 +void *supported_file;
 +unsigned long supported_size;
 +
 +static const char *supported(struct module *mod)
 +{
 +      unsigned long pos = 0;
 +      char *line;
 +
 +      /* In a first shot, do a simple linear scan. */
 +      while ((line = get_next_line(&pos, supported_file,
 +                                   supported_size))) {
 +              const char *basename, *how = "yes";
 +              char *l = line;
 +
 +              /* optional type-of-support flag */
 +              for (l = line; *l != '\0'; l++) {
 +                      if (*l == ' ' || *l == '\t') {
 +                              *l = '\0';
 +                              how = l + 1;
 +                              break;
 +                      }
 +              }
 +
 +              /* skip directory components */
 +              if ((l = strrchr(line, '/')))
 +                      line = l + 1;
 +              /* strip .ko extension */
 +              l = line + strlen(line);
 +              if (l - line > 3 && !strcmp(l-3, ".ko"))
 +                      *(l-3) = '\0';
 +
 +              /* skip directory components */
 +              if ((basename = strrchr(mod->name, '/')))
 +                      basename++;
 +              else
 +                      basename = mod->name;
 +              if (!strcmp(basename, line))
 +                      return how;
 +      }
 +      return NULL;
 +}
 +
  static void read_symbols(char *modname)
  {
        const char *symname;
@@@ -1905,13 -1872,6 +1914,13 @@@ static void add_staging_flag(struct buf
                buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
  }
  
 +static void add_supported_flag(struct buffer *b, struct module *mod)
 +{
 +      const char *how = supported(mod);
 +      if (how)
 +              buf_printf(b, "\nMODULE_INFO(supported, \"%s\");\n", how);
 +}
 +
  /**
   * Record CRCs for unresolved symbols
   **/
@@@ -2052,13 -2012,6 +2061,13 @@@ static void write_if_changed(struct buf
        fclose(file);
  }
  
 +static void read_supported(const char *fname)
 +{
 +      supported_file = grab_file(fname, &supported_size);
 +      if (!supported_file)
 +              ; /* ignore error */
 +}
 +
  /* parse Module.symvers file. line format:
   * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
   **/
@@@ -2152,13 -2105,12 +2161,13 @@@ int main(int argc, char **argv
        struct buffer buf = { };
        char *kernel_read = NULL, *module_read = NULL;
        char *dump_write = NULL;
 +      const char *supported = NULL;
        int opt;
        int err;
        struct ext_sym_list *extsym_iter;
        struct ext_sym_list *extsym_start = NULL;
  
 -      while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:")) != -1) {
 +      while ((opt = getopt(argc, argv, "i:I:e:cmsSo:awM:K:N:")) != -1) {
                switch (opt) {
                case 'i':
                        kernel_read = optarg;
                case 'w':
                        warn_unresolved = 1;
                        break;
 +              case 'N':
 +                      supported = optarg;
 +                      break;
                default:
                        exit(1);
                }
        }
  
 +      if (supported)
 +              read_supported(supported);
        if (kernel_read)
                read_dump(kernel_read, 1);
        if (module_read)
                add_header(&buf, mod);
                add_intree_flag(&buf, !external_module);
                add_staging_flag(&buf, mod->name);
 +              add_supported_flag(&buf, mod);
                err |= add_versions(&buf, mod);
                add_depends(&buf, mod, modules);
                add_moddevtable(&buf, mod);