Print this page
XXXXX tmpfs can be induced to deadlock


  38 #include <sys/stat.h>
  39 #include <sys/debug.h>
  40 #include <sys/policy.h>
  41 #include <sys/fs/tmpnode.h>
  42 #include <sys/fs/tmp.h>
  43 #include <sys/vtrace.h>
  44 
  45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
  46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
  47         char *, struct tmpnode *, struct tdirent *, struct cred *);
  48 static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
  49 static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
  50         enum de_op, struct tmpnode **, struct cred *);
  51 static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
  52         enum de_op, struct tmpnode *);
  53 
  54 
  55 #define T_HASH_SIZE     8192            /* must be power of 2 */
  56 #define T_MUTEX_SIZE    64
  57 





  58 static struct tdirent   *t_hashtable[T_HASH_SIZE];
  59 static kmutex_t          t_hashmutex[T_MUTEX_SIZE];
  60 
  61 #define T_HASH_INDEX(a)         ((a) & (T_HASH_SIZE-1))
  62 #define T_MUTEX_INDEX(a)        ((a) & (T_MUTEX_SIZE-1))
  63 
  64 #define TMPFS_HASH(tp, name, hash)                              \
  65         {                                                       \
  66                 char Xc, *Xcp;                                  \
  67                 hash = (uint_t)(uintptr_t)(tp) >> 8;              \
  68                 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)     \
  69                         hash = (hash << 4) + hash + (uint_t)Xc;   \
  70         }
  71 
  72 void
  73 tmpfs_hash_init(void)
  74 {
  75         int     ix;
  76 
  77         for (ix = 0; ix < T_MUTEX_SIZE; ix++)


 250          */
 251         ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
 252         ASSERT(dir->tn_type == VDIR);
 253 
 254         /*
 255          * Don't allow '/' characters in pathname component
 256          * (thus in ufs_direnter()).
 257          */
 258         for (s = name; *s; s++)
 259                 if (*s == '/')
 260                         return (EACCES);
 261 
 262         if (name[0] == '\0')
 263                 panic("tdirenter: NULL name");
 264 
 265         /*
 266          * For link and rename lock the source entry and check the link count
 267          * to see if it has been removed while it was unlocked.
 268          */
 269         if (op == DE_LINK || op == DE_RENAME) {
 270                 if (tp != dir)
 271                         rw_enter(&tp->tn_rwlock, RW_WRITER);

























































 272                 mutex_enter(&tp->tn_tlock);
 273                 if (tp->tn_nlink == 0) {
 274                         mutex_exit(&tp->tn_tlock);
 275                         if (tp != dir)
 276                                 rw_exit(&tp->tn_rwlock);
 277                         return (ENOENT);
 278                 }
 279 
 280                 if (tp->tn_nlink == MAXLINK) {
 281                         mutex_exit(&tp->tn_tlock);
 282                         if (tp != dir)
 283                                 rw_exit(&tp->tn_rwlock);
 284                         return (EMLINK);
 285                 }
 286                 tp->tn_nlink++;
 287                 gethrestime(&tp->tn_ctime);
 288                 mutex_exit(&tp->tn_tlock);
 289                 if (tp != dir)
 290                         rw_exit(&tp->tn_rwlock);
 291         }




  38 #include <sys/stat.h>
  39 #include <sys/debug.h>
  40 #include <sys/policy.h>
  41 #include <sys/fs/tmpnode.h>
  42 #include <sys/fs/tmp.h>
  43 #include <sys/vtrace.h>
  44 
  45 static int tdircheckpath(struct tmpnode *, struct tmpnode *, struct cred *);
  46 static int tdirrename(struct tmpnode *, struct tmpnode *, struct tmpnode *,
  47         char *, struct tmpnode *, struct tdirent *, struct cred *);
  48 static void tdirfixdotdot(struct tmpnode *, struct tmpnode *, struct tmpnode *);
  49 static int tdirmaketnode(struct tmpnode *, struct tmount *, struct vattr *,
  50         enum de_op, struct tmpnode **, struct cred *);
  51 static int tdiraddentry(struct tmpnode *, struct tmpnode *, char *,
  52         enum de_op, struct tmpnode *);
  53 
  54 
  55 #define T_HASH_SIZE     8192            /* must be power of 2 */
  56 #define T_MUTEX_SIZE    64
  57 
  58 /* Non-static so compilers won't constant-fold these away. */
  59 clock_t tmpfs_rename_backoff_delay = 1;
  60 unsigned int tmpfs_rename_backoff_tries = 0;
  61 unsigned long tmpfs_rename_loops = 0;
  62 
  63 static struct tdirent   *t_hashtable[T_HASH_SIZE];
  64 static kmutex_t          t_hashmutex[T_MUTEX_SIZE];
  65 
  66 #define T_HASH_INDEX(a)         ((a) & (T_HASH_SIZE-1))
  67 #define T_MUTEX_INDEX(a)        ((a) & (T_MUTEX_SIZE-1))
  68 
  69 #define TMPFS_HASH(tp, name, hash)                              \
  70         {                                                       \
  71                 char Xc, *Xcp;                                  \
  72                 hash = (uint_t)(uintptr_t)(tp) >> 8;              \
  73                 for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)     \
  74                         hash = (hash << 4) + hash + (uint_t)Xc;   \
  75         }
  76 
  77 void
  78 tmpfs_hash_init(void)
  79 {
  80         int     ix;
  81 
  82         for (ix = 0; ix < T_MUTEX_SIZE; ix++)


 255          */
 256         ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
 257         ASSERT(dir->tn_type == VDIR);
 258 
 259         /*
 260          * Don't allow '/' characters in pathname component
 261          * (thus in ufs_direnter()).
 262          */
 263         for (s = name; *s; s++)
 264                 if (*s == '/')
 265                         return (EACCES);
 266 
 267         if (name[0] == '\0')
 268                 panic("tdirenter: NULL name");
 269 
 270         /*
 271          * For link and rename lock the source entry and check the link count
 272          * to see if it has been removed while it was unlocked.
 273          */
 274         if (op == DE_LINK || op == DE_RENAME) {
 275                 if (tp != dir) {
 276                         unsigned int tries = 0;
 277 
 278                         /*
 279                          * If we are acquiring tp->tn_rwlock (for SOURCE)
 280                          * inside here, we must consider the following:
 281                          *
 282                          * - dir->tn_rwlock (TARGET) is already HELD (see
 283                          * above ASSERT()).
 284                          *
 285                          * - It is possible our SOURCE is a parent of our
 286                          * TARGET. Yes it's unusual, but it will return an
 287                          * error below via tdircheckpath().
 288                          *
 289                          * - It is also possible that another thread,
 290                          * concurrent to this one, is performing
 291                          * rmdir(TARGET), which means it will first acquire
 292                          * SOURCE's lock, THEN acquire TARGET's lock, which
 293                          * could result in this thread holding TARGET and
 294                          * trying for SOURCE, but the other thread holding
 295                          * SOURCE and trying for TARGET.  This is deadlock,
 296                          * and it's inducible.
 297                          *
 298                          * To prevent this, we borrow some techniques from UFS
 299                          * and rw_tryenter(), delaying if we fail, and
 300                          * if someone tweaks the number of backoff tries to be
 301                          * nonzero, return EBUSY after that number of tries.
 302                          */
 303                         while (!rw_tryenter(&tp->tn_rwlock, RW_WRITER)) {
 304                                 /*
 305                                  * Sloppy, but this is a diagnostic so atomic
 306                                  * increment would be overkill.
 307                                  */
 308                                 tmpfs_rename_loops++;
 309 
 310                                 if (tmpfs_rename_backoff_tries != 0) {
 311                                         if (tries > tmpfs_rename_backoff_tries)
 312                                                 return (EBUSY);
 313                                         tries++;
 314                                 }
 315                                 /*
 316                                  * NOTE: We're still holding dir->tn_rwlock,
 317                                  * so drop it over the delay, so any other
 318                                  * thread can get its business done.
 319                                  *
 320                                  * No state change or state inspection happens
 321                                  * prior to here, so it is not wholly dangerous
 322                                  * to release-and-reacquire dir->tn_rwlock.
 323                                  *
 324                                  * Hold the vnode of dir in case it gets
 325                                  * released by another thread, though.
 326                                  */
 327                                 VN_HOLD(TNTOV(dir));
 328                                 rw_exit(&dir->tn_rwlock);
 329                                 delay(tmpfs_rename_backoff_delay);
 330                                 rw_enter(&dir->tn_rwlock, RW_WRITER);
 331                                 VN_RELE(TNTOV(dir));
 332                         }
 333                 }
 334                 mutex_enter(&tp->tn_tlock);
 335                 if (tp->tn_nlink == 0) {
 336                         mutex_exit(&tp->tn_tlock);
 337                         if (tp != dir)
 338                                 rw_exit(&tp->tn_rwlock);
 339                         return (ENOENT);
 340                 }
 341 
 342                 if (tp->tn_nlink == MAXLINK) {
 343                         mutex_exit(&tp->tn_tlock);
 344                         if (tp != dir)
 345                                 rw_exit(&tp->tn_rwlock);
 346                         return (EMLINK);
 347                 }
 348                 tp->tn_nlink++;
 349                 gethrestime(&tp->tn_ctime);
 350                 mutex_exit(&tp->tn_tlock);
 351                 if (tp != dir)
 352                         rw_exit(&tp->tn_rwlock);
 353         }