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

*** 18,27 **** --- 18,28 ---- * * CDDL HEADER END */ /* + * Copyright 2014 Coraid, Inc. * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file implements /dev filesystem operations for non-global
*** 653,707 **** name, name); } } /* ! * Build directory vnodes based on the profile and the global ! * dev instance. */ ! void ! prof_filldir(struct sdev_node *ddv) { ! int firsttime = 1; ! struct sdev_node *gdir = ddv->sdev_origin; ! ASSERT(RW_READ_HELD(&ddv->sdev_contents)); /* * We need to rebuild the directory content if ! * - SDEV_BUILD is set ! * - The device tree generation number has changed * - The corresponding /dev namespace has been updated */ ! check_build: ! if ((ddv->sdev_flags & SDEV_BUILD) == 0 && ! ddv->sdev_devtree_gen == devtree_gen && ! (gdir == NULL || ddv->sdev_ldir_gen ! == gdir->sdev_gdir_gen)) ! return; /* already up to date */ ! /* We may have become a zombie (across a try) */ ! if (ddv->sdev_state == SDEV_ZOMBIE) ! return; ! if (firsttime && rw_tryupgrade(&ddv->sdev_contents) == 0) { rw_exit(&ddv->sdev_contents); - firsttime = 0; rw_enter(&ddv->sdev_contents, RW_WRITER); ! goto check_build; } sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); ! if (gdir) sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_ldir_gen, gdir->sdev_gdir_gen)); /* update flags and generation number so next filldir is quick */ ddv->sdev_flags &= ~SDEV_BUILD; ddv->sdev_devtree_gen = devtree_gen; ! if (gdir) ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; prof_make_symlinks(ddv); prof_make_maps(ddv); prof_make_names(ddv); --- 654,742 ---- name, name); } } /* ! * Return True if directory cache is out of date and should be updated. */ ! static boolean_t ! prof_dev_needupdate(sdev_node_t *ddv) { ! sdev_node_t *gdir = ddv->sdev_origin; ! /* ! * Caller can have either reader or writer lock ! */ ! ASSERT(RW_LOCK_HELD(&ddv->sdev_contents)); /* * We need to rebuild the directory content if ! * - ddv is not in a SDEV_ZOMBIE state ! * - SDEV_BUILD is set OR ! * - The device tree generation number has changed OR * - The corresponding /dev namespace has been updated */ ! return ((ddv->sdev_state != SDEV_ZOMBIE) && ! (((ddv->sdev_flags & SDEV_BUILD) != 0) || ! (ddv->sdev_devtree_gen != devtree_gen) || ! ((gdir != NULL) && ! (ddv->sdev_ldir_gen != gdir->sdev_gdir_gen)))); ! } ! /* ! * Build directory vnodes based on the profile and the global ! * dev instance. ! */ ! void ! prof_filldir(sdev_node_t *ddv) ! { ! sdev_node_t *gdir; ! ASSERT(RW_READ_HELD(&ddv->sdev_contents)); ! ! if (!prof_dev_needupdate(ddv)) { ! ASSERT(RW_READ_HELD(&ddv->sdev_contents)); ! return; ! } ! /* ! * Upgrade to writer lock ! */ ! if (rw_tryupgrade(&ddv->sdev_contents) == 0) { ! /* ! * We need to drop the read lock and re-acquire it as a ! * write lock. While we do this the condition may change so we ! * need to re-check condition. ! * NOTE: if device becomes a zombie, prof_dev_needupdate returns ! * false, so nothing we will just return in this case. ! */ rw_exit(&ddv->sdev_contents); rw_enter(&ddv->sdev_contents, RW_WRITER); ! if (!prof_dev_needupdate(ddv)) { ! /* Downgrade back to the read lock before returning */ ! rw_downgrade(&ddv->sdev_contents); ! return; } + } + /* At this point we should have a write lock */ + ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); + sdcmn_err10(("devtree_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_devtree_gen, devtree_gen)); ! ! gdir = ddv->sdev_origin; ! ! if (gdir != NULL) sdcmn_err10(("sdev_dir_gen (%s): %ld -> %ld\n", ddv->sdev_path, ddv->sdev_ldir_gen, gdir->sdev_gdir_gen)); /* update flags and generation number so next filldir is quick */ + if ((ddv->sdev_flags & SDEV_BUILD) == SDEV_BUILD) { ddv->sdev_flags &= ~SDEV_BUILD; + } ddv->sdev_devtree_gen = devtree_gen; ! if (gdir != NULL) ddv->sdev_ldir_gen = gdir->sdev_gdir_gen; prof_make_symlinks(ddv); prof_make_maps(ddv); prof_make_names(ddv);