> Date: Fri, 17 Mar 2023 15:23:19 +0100
> From: Moritz Buhl <mb...@openbsd.org>
> 
> Dear tech,
> 
> I have a machine with some kind of hardware defect.
> Smartctl shows that one SSD regularly has an unexpected power loss
> (Attribute 174):
> 
> 174 Unknown_Attribute       0x0032   100   100   000    Old_age
> Always       -       284.
> 
> On the console I see the following output:
> 
> root on sd0a (b75e60dd651bd99e.a) swap on sd0b dump on sd0b
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> ahci0: unrecoverable errors (IS: 10<UFS>), disabling port.
> ahci0: device didn't come ready after reset, TFD: 0x1d0<BSY>
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: stopping the port, softreset slot 31 was still active.
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> ahci0: device didn't come ready after reset, TFD: 0x441<ERR>
> mode = 0100644, inum = 7731315, fs = /var
> panic: ffs_valloc: dup alloc
> Stopped at      db_enter+0x10:  popq    %rbp
>     TID    PID    UID     PRFLAGS     PFLAGS  CPU  COMMAND
>  175271  46390   1001         0x2  0x4000000    0  rustc
>  345274  46390   1001         0x2  0x4000000    1  rustc
>  224769  52321   1001         0x2  0x4000000    5  rustc
> *414814  59107   1001      0x1002  0x4080000    2K rustc
>   87951  59107   1001      0x1002  0x4080000    4  rustc
> db_enter() at db_enter+0x10
> panic(ffffffff81f194f6) at panic+0xbf
> ffs_inode_alloc(fffffd87b0b8c1e0,81a4,fffffd8fc6cfc5d0,ffff800020a7a948) at 
> ffs
> _inode_alloc+0x42e
> ufs_makeinode(81a4,fffffd87ab540438,ffff800020a7ac40,ffff800020a7ac70) at 
> ufs_m
> akeinode+0x79
> ufs_create(ffff800020a7a9f8) at ufs_create+0x3c
> VOP_CREATE(fffffd87ab540438,ffff800020a7ac40,ffff800020a7ac70,ffff800020a7aa50)
>  at VOP_CREATE+0x3f
> vn_open(ffff800020a7ac10,10602,1a4) at vn_open+0x162
> doopenat(ffff800020879cf0,ffffff9c,246e5646200,10601,1b6,ffff800020a7adf0) at 
> d
> oopenat+0x1cd
> syscall(ffff800020a7ae60) at syscall+0x35f
> Xsyscall() at Xsyscall+0x128
> end of kernel
> 
> ffs_inode_alloc checks the mode of the newly allocated inode and
> if it is not 0 it panics.
> I suspect that due to the ssds softreset that IO operations take
> longer and cause trouble due to missing locking.
> 
> For example in ffs_nodealloccg it is possible for an FFS2 file
> system to sleep due to a getblk. During the sleep, another call to
> ffs_nodealloccg might happen with the same ipref.
> The function first checks if there is space at the prefered location,
> then sleeps and then reserves it. This is a TOCTOU bug.
> 
> FreeBSD fixed this in 2013:
> https://github.com/freebsd/freebsd-src/commit/94f4ac214c403b99cbc44dd6e9cdf4db2cc9b297
> 
> Below is a diff that addresses this with a exclusive lock
> using RW_SLEEPFAIL.
> 
> Index: ufs/ffs/ffs_alloc.c
> ===================================================================
> RCS file: /cvs/src/sys/ufs/ffs/ffs_alloc.c,v
> retrieving revision 1.114
> diff -u -p -r1.114 ffs_alloc.c
> --- ufs/ffs/ffs_alloc.c       11 Mar 2021 13:31:35 -0000      1.114
> +++ ufs/ffs/ffs_alloc.c       10 Mar 2023 15:44:52 -0000
> @@ -46,6 +46,7 @@
>  #include <sys/buf.h>
>  #include <sys/vnode.h>
>  #include <sys/mount.h>
> +#include <sys/rwlock.h>
>  #include <sys/syslog.h>
>  #include <sys/stdint.h>
>  #include <sys/time.h>
> @@ -1089,6 +1090,7 @@ gotit:
>  }
>  
>  /* inode allocation routine */
> +struct rwlock ffs_node_lock = RWLOCK_INITIALIZER("indallck");

Don't hide a global variable like that!

>  daddr_t
>  ffs_nodealloccg(struct inode *ip, u_int cg, daddr_t ipref, int mode)
>  {
> @@ -1115,6 +1117,9 @@ ffs_nodealloccg(struct inode *ip, u_int 
>               return (0);
>  
>       cgp = (struct cg *)bp->b_data;
> +#ifdef FFS2
> + check_nifree:
> +#endif
>       if (cgp->cg_cs.cs_nifree == 0) {
>               brelse(bp);
>               return (0);
> @@ -1201,9 +1206,12 @@ gotit:
>           /* Has any inode not been used at least once? */
>           cgp->cg_initediblk < cgp->cg_ffs2_niblk) {
>  
> +             if (rw_enter(&ffs_node_lock, RW_WRITE | RW_SLEEPFAIL))
> +                     goto check_nifree;
>                  ibp = getblk(ip->i_devvp, fsbtodb(fs,
>                      ino_to_fsba(fs, cg * fs->fs_ipg + cgp->cg_initediblk)),
>                      (int)fs->fs_bsize, 0, INFSLP);
> +             rw_exit(&ffs_node_lock);
>  
>                  memset(ibp->b_data, 0, fs->fs_bsize);
>                  dp2 = (struct ufs2_dinode *)(ibp->b_data);
> 
> 

Reply via email to