Index: sys/rwlock.h =================================================================== RCS file: /cvs/src/sys/sys/rwlock.h,v diff -u -p -r1.29 rwlock.h --- sys/rwlock.h 27 Nov 2024 01:02:03 -0000 1.29 +++ sys/rwlock.h 29 Nov 2024 07:28:35 -0000 @@ -110,6 +110,7 @@ struct rwlock { #define RW_WRITE 0x0001UL /* exclusive lock */ #define RW_READ 0x0002UL /* shared lock */ #define RW_DOWNGRADE 0x0004UL /* downgrade exclusive to shared */ +#define RW_UPGRADE 0x0005UL #define RW_OPMASK 0x0007UL #define RW_INTR 0x0010UL /* interruptible sleep */ Index: kern/kern_rwlock.c =================================================================== RCS file: /cvs/src/sys/kern/kern_rwlock.c,v diff -u -p -r1.51 kern_rwlock.c --- kern/kern_rwlock.c 27 Nov 2024 01:02:03 -0000 1.51 +++ kern/kern_rwlock.c 29 Nov 2024 07:28:36 -0000 @@ -74,6 +74,7 @@ static int rw_do_enter_read(struct rwloc static void rw_do_exit_read(struct rwlock *, unsigned long); static int rw_do_enter_write(struct rwlock *, int); static int rw_downgrade(struct rwlock *, int); +static int rw_upgrade(struct rwlock *, int); static void rw_exited(struct rwlock *); @@ -203,6 +204,9 @@ rw_enter(struct rwlock *rwl, int flags) case RW_DOWNGRADE: error = rw_downgrade(rwl, flags); break; + case RW_UPGRADE: + error = rw_upgrade(rwl, flags); + break; default: panic("%s rwlock %p: %s unexpected op 0x%x", rwl->rwl_name, rwl, __func__, op); @@ -246,6 +250,7 @@ rw_do_enter_write(struct rwlock *rwl, in * can progress. Hence no spinning if we hold the kernel lock. */ if (!_kernel_lock_held()) { + struct schedstate_percpu *spc = &curcpu()->ci_schedstate; int spins; /* @@ -253,6 +258,7 @@ rw_do_enter_write(struct rwlock *rwl, in * is acquired by writer. */ + spc->spc_spinning++; for (spins = 0; spins < RW_SPINS; spins++) { CPU_BUSY_CYCLE(); owner = atomic_load_long(&rwl->rwl_owner); @@ -261,10 +267,12 @@ rw_do_enter_write(struct rwlock *rwl, in owner = rw_cas(&rwl->rwl_owner, 0, self); if (owner == 0) { + spc->spc_spinning--; /* ok, we won now. */ goto locked; } } + spc->spc_spinning--; } #endif @@ -430,6 +438,41 @@ rw_downgrade(struct rwlock *rwl, int fla if (atomic_load_int(&rwl->rwl_waiters) == 0 && atomic_load_int(&rwl->rwl_readers) > 0) wakeup(&rwl->rwl_readers); + + return (0); +} + +static int +rw_upgrade(struct rwlock *rwl, int flags) +{ + unsigned long self = rw_self(); + unsigned long owner; + + KASSERTMSG(ISSET(flags, RW_NOSLEEP), "RW_UPGRADE without RW_NOSLEEP"); + + owner = atomic_cas_ulong(&rwl->rwl_owner, RWLOCK_READ_INCR, self); + if (owner != RWLOCK_READ_INCR) { + if (__predict_false(owner == 0)) { + panic("%s rwlock %p: upgrade on unowned lock", + rwl->rwl_name, rwl); + } + if (__predict_false(ISSET(owner, RWLOCK_WRLOCK))) { + panic("%s rwlock %p: upgrade on write locked lock" + "(owner 0x%lx, self 0x%lx)", rwl->rwl_name, rwl, + owner, self); + } + + return (EBUSY); + } + +#ifdef WITNESS + { + int lop_flags = LOP_NEWORDER; + if (ISSET(flags, RW_DUPOK)) + lop_flags |= LOP_DUPOK; + WITNESS_UPGRADE(&rwl->rwl_lock_obj, lop_flags); + } +#endif return (0); }