Print this page
5360 Race condition in devfs upgrades reader to writer incidentally and causes panic


   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*

  23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * This file implements /dev filesystem operations for non-global
  28  * instances. Three major entry points:
  29  * devname_profile_update()
  30  *   Update matching rules determining which names to export
  31  * prof_readdir()
  32  *   Return the list of exported names
  33  * prof_lookup()
  34  *   Implements lookup
  35  */
  36 
  37 #include <sys/types.h>
  38 #include <sys/param.h>
  39 #include <sys/sysmacros.h>
  40 #include <sys/vnode.h>
  41 #include <sys/uio.h>
  42 #include <sys/dirent.h>


 638         }
 639 
 640         /* Walk nvlist and lookup corresponding device in global inst */
 641         while (nvp = nvlist_next_nvpair(nvl, nvp)) {
 642                 int type;
 643                 rv = nvpair_value_int32(nvp, &type);
 644                 if (rv != 0) {
 645                         cmn_err(CE_WARN, sdev_nvp_val_err,
 646                             rv, nvpair_name(nvp));
 647                         break;
 648                 }
 649                 if (type == PROFILE_TYPE_EXCLUDE)
 650                         continue;
 651                 name = nvpair_name(nvp);
 652                 (void) prof_lookup_globaldev(dir, dir->sdev_origin,
 653                     name, name);
 654         }
 655 }
 656 
 657 /*
 658  * Build directory vnodes based on the profile and the global
 659  * dev instance.
 660  */
 661 void
 662 prof_filldir(struct sdev_node *ddv)
 663 {
 664         int firsttime = 1;
 665         struct sdev_node *gdir = ddv->sdev_origin;
 666 
 667         ASSERT(RW_READ_HELD(&ddv->sdev_contents));



 668 
 669         /*
 670          * We need to rebuild the directory content if
 671          * - SDEV_BUILD is set
 672          * - The device tree generation number has changed

 673          * - The corresponding /dev namespace has been updated
 674          */
 675 check_build:
 676         if ((ddv->sdev_flags & SDEV_BUILD) == 0 &&
 677             ddv->sdev_devtree_gen == devtree_gen &&
 678             (gdir == NULL || ddv->sdev_ldir_gen
 679             == gdir->sdev_gdir_gen))
 680                 return;         /* already up to date */
 681 
 682         /* We may have become a zombie (across a try) */
 683         if (ddv->sdev_state == SDEV_ZOMBIE)
 684                 return;





 685 
 686         if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) {
















 687                 rw_exit(&ddv->sdev_contents);
 688                 firsttime = 0;
 689                 rw_enter(&ddv->sdev_contents, RW_WRITER);
 690                 goto check_build;



 691         }




 692         sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
 693             ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
 694         if (gdir)



 695                 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
 696                     ddv->sdev_path, ddv->sdev_ldir_gen,
 697                     gdir->sdev_gdir_gen));
 698 
 699         /* update flags and generation number so next filldir is quick */

 700         ddv->sdev_flags &= ~SDEV_BUILD;

 701         ddv->sdev_devtree_gen = devtree_gen;
 702         if (gdir)
 703                 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
 704 
 705         prof_make_symlinks(ddv);
 706         prof_make_maps(ddv);
 707         prof_make_names(ddv);
 708         rw_downgrade(&ddv->sdev_contents);
 709 }
 710 
 711 /* apply include/exclude pattern to existing directory content */
 712 static void
 713 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
 714 {
 715         struct sdev_node *dv;
 716 
 717         /* leaf pattern */
 718         if (pathleft == NULL) {
 719                 if (type == PROFILE_TYPE_INCLUDE)
 720                         return; /* nothing to do for include */
 721                 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
 722                 return;




   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2014 Coraid, Inc.
  24  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  25  */
  26 
  27 /*
  28  * This file implements /dev filesystem operations for non-global
  29  * instances. Three major entry points:
  30  * devname_profile_update()
  31  *   Update matching rules determining which names to export
  32  * prof_readdir()
  33  *   Return the list of exported names
  34  * prof_lookup()
  35  *   Implements lookup
  36  */
  37 
  38 #include <sys/types.h>
  39 #include <sys/param.h>
  40 #include <sys/sysmacros.h>
  41 #include <sys/vnode.h>
  42 #include <sys/uio.h>
  43 #include <sys/dirent.h>


 639         }
 640 
 641         /* Walk nvlist and lookup corresponding device in global inst */
 642         while (nvp = nvlist_next_nvpair(nvl, nvp)) {
 643                 int type;
 644                 rv = nvpair_value_int32(nvp, &type);
 645                 if (rv != 0) {
 646                         cmn_err(CE_WARN, sdev_nvp_val_err,
 647                             rv, nvpair_name(nvp));
 648                         break;
 649                 }
 650                 if (type == PROFILE_TYPE_EXCLUDE)
 651                         continue;
 652                 name = nvpair_name(nvp);
 653                 (void) prof_lookup_globaldev(dir, dir->sdev_origin,
 654                     name, name);
 655         }
 656 }
 657 
 658 /*
 659  * Return True if directory cache is out of date and should be updated.

 660  */
 661 static boolean_t
 662 prof_dev_needupdate(sdev_node_t *ddv)
 663 {
 664         sdev_node_t *gdir = ddv->sdev_origin;

 665 
 666         /*
 667          * Caller can have either reader or writer lock
 668          */
 669         ASSERT(RW_LOCK_HELD(&ddv->sdev_contents));
 670 
 671         /*
 672          * We need to rebuild the directory content if
 673          * - ddv is not in a SDEV_ZOMBIE state
 674          * - SDEV_BUILD is set OR
 675          * - The device tree generation number has changed OR
 676          * - The corresponding /dev namespace has been updated
 677          */
 678         return ((ddv->sdev_state != SDEV_ZOMBIE) &&
 679             (((ddv->sdev_flags & SDEV_BUILD) != 0) ||
 680             (ddv->sdev_devtree_gen != devtree_gen) ||
 681             ((gdir != NULL) &&
 682             (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen))));
 683 }
 684 
 685 /*
 686  * Build directory vnodes based on the profile and the global
 687  * dev instance.
 688  */
 689 void
 690 prof_filldir(sdev_node_t *ddv)
 691 {
 692         sdev_node_t *gdir;
 693 
 694         ASSERT(RW_READ_HELD(&ddv->sdev_contents));
 695 
 696         if (!prof_dev_needupdate(ddv)) {
 697                 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
 698                 return;
 699         }
 700         /*
 701          * Upgrade to writer lock
 702          */
 703         if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
 704                 /*
 705                  * We need to drop the read lock and re-acquire it as a
 706                  * write lock. While we do this the condition may change so we
 707                  * need to re-check condition.
 708                  * NOTE: if device becomes a zombie, prof_dev_needupdate returns
 709                  * false, so nothing we will just return in this case.
 710                  */
 711                 rw_exit(&ddv->sdev_contents);

 712                 rw_enter(&ddv->sdev_contents, RW_WRITER);
 713                 if (!prof_dev_needupdate(ddv)) {
 714                         /* Downgrade back to the read lock before returning */
 715                         rw_downgrade(&ddv->sdev_contents);
 716                         return;
 717                 }
 718         }
 719         /* At this point we should have a write lock */
 720         ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
 721 
 722         sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n",
 723             ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen));
 724 
 725         gdir = ddv->sdev_origin;
 726 
 727         if (gdir != NULL)
 728                 sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n",
 729                     ddv->sdev_path, ddv->sdev_ldir_gen,
 730                     gdir->sdev_gdir_gen));
 731 
 732         /* update flags and generation number so next filldir is quick */
 733         if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) {
 734                 ddv->sdev_flags &= ~SDEV_BUILD;
 735         }
 736         ddv->sdev_devtree_gen = devtree_gen;
 737         if (gdir != NULL)
 738                 ddv->sdev_ldir_gen = gdir->sdev_gdir_gen;
 739 
 740         prof_make_symlinks(ddv);
 741         prof_make_maps(ddv);
 742         prof_make_names(ddv);
 743         rw_downgrade(&ddv->sdev_contents);
 744 }
 745 
 746 /* apply include/exclude pattern to existing directory content */
 747 static void
 748 apply_dir_pattern(struct sdev_node *dir, char *expr, char *pathleft, int type)
 749 {
 750         struct sdev_node *dv;
 751 
 752         /* leaf pattern */
 753         if (pathleft == NULL) {
 754                 if (type == PROFILE_TYPE_INCLUDE)
 755                         return; /* nothing to do for include */
 756                 (void) sdev_cleandir(dir, expr, SDEV_ENFORCE);
 757                 return;