Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

workqueue: clean up WORK_* constant types, clarify masking

Dave Airlie reports that gcc-13.1.1 has started complaining about some
of the workqueue code in 32-bit arm builds:

kernel/workqueue.c: In function ‘get_work_pwq’:
kernel/workqueue.c:713:24: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
713 | return (void *)(data & WORK_STRUCT_WQ_DATA_MASK);
| ^
[ ... a couple of other cases ... ]

and while it's not immediately clear exactly why gcc started complaining
about it now, I suspect it's some C23-induced enum type handlign fixup in
gcc-13 is the cause.

Whatever the reason for starting to complain, the code and data types
are indeed disgusting enough that the complaint is warranted.

The wq code ends up creating various "helper constants" (like that
WORK_STRUCT_WQ_DATA_MASK) using an enum type, which is all kinds of
confused. The mask needs to be 'unsigned long', not some unspecified
enum type.

To make matters worse, the actual "mask and cast to a pointer" is
repeated a couple of times, and the cast isn't even always done to the
right pointer, but - as the error case above - to a 'void *' with then
the compiler finishing the job.

That's now how we roll in the kernel.

So create the masks using the proper types rather than some ambiguous
enumeration, and use a nice helper that actually does the type
conversion in one well-defined place.

Incidentally, this magically makes clang generate better code. That,
admittedly, is really just a sign of clang having been seriously
confused before, and cleaning up the typing unconfuses the compiler too.

Reported-by: Dave Airlie <airlied@gmail.com>
Link: https://lore.kernel.org/lkml/CAPM=9twNnV4zMCvrPkw3H-ajZOH-01JVh_kDrxdPYQErz8ZTdA@mail.gmail.com/
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Tejun Heo <tj@kernel.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+16 -12
+8 -7
include/linux/workqueue.h
··· 68 68 WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT, 69 69 70 70 __WORK_OFFQ_CANCELING = WORK_OFFQ_FLAG_BASE, 71 - WORK_OFFQ_CANCELING = (1 << __WORK_OFFQ_CANCELING), 72 71 73 72 /* 74 73 * When a work item is off queue, its high bits point to the last ··· 78 79 WORK_OFFQ_POOL_SHIFT = WORK_OFFQ_FLAG_BASE + WORK_OFFQ_FLAG_BITS, 79 80 WORK_OFFQ_LEFT = BITS_PER_LONG - WORK_OFFQ_POOL_SHIFT, 80 81 WORK_OFFQ_POOL_BITS = WORK_OFFQ_LEFT <= 31 ? WORK_OFFQ_LEFT : 31, 81 - WORK_OFFQ_POOL_NONE = (1LU << WORK_OFFQ_POOL_BITS) - 1, 82 - 83 - /* convenience constants */ 84 - WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1, 85 - WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK, 86 - WORK_STRUCT_NO_POOL = (unsigned long)WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT, 87 82 88 83 /* bit mask for work_busy() return values */ 89 84 WORK_BUSY_PENDING = 1 << 0, ··· 86 93 /* maximum string length for set_worker_desc() */ 87 94 WORKER_DESC_LEN = 24, 88 95 }; 96 + 97 + /* Convenience constants - of type 'unsigned long', not 'enum'! */ 98 + #define WORK_OFFQ_CANCELING (1ul << __WORK_OFFQ_CANCELING) 99 + #define WORK_OFFQ_POOL_NONE ((1ul << WORK_OFFQ_POOL_BITS) - 1) 100 + #define WORK_STRUCT_NO_POOL (WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT) 101 + 102 + #define WORK_STRUCT_FLAG_MASK ((1ul << WORK_STRUCT_FLAG_BITS) - 1) 103 + #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) 89 104 90 105 struct work_struct { 91 106 atomic_long_t data;
+8 -5
kernel/workqueue.c
··· 705 705 set_work_data(work, WORK_STRUCT_NO_POOL, 0); 706 706 } 707 707 708 + static inline struct pool_workqueue *work_struct_pwq(unsigned long data) 709 + { 710 + return (struct pool_workqueue *)(data & WORK_STRUCT_WQ_DATA_MASK); 711 + } 712 + 708 713 static struct pool_workqueue *get_work_pwq(struct work_struct *work) 709 714 { 710 715 unsigned long data = atomic_long_read(&work->data); 711 716 712 717 if (data & WORK_STRUCT_PWQ) 713 - return (void *)(data & WORK_STRUCT_WQ_DATA_MASK); 718 + return work_struct_pwq(data); 714 719 else 715 720 return NULL; 716 721 } ··· 743 738 assert_rcu_or_pool_mutex(); 744 739 745 740 if (data & WORK_STRUCT_PWQ) 746 - return ((struct pool_workqueue *) 747 - (data & WORK_STRUCT_WQ_DATA_MASK))->pool; 741 + return work_struct_pwq(data)->pool; 748 742 749 743 pool_id = data >> WORK_OFFQ_POOL_SHIFT; 750 744 if (pool_id == WORK_OFFQ_POOL_NONE) ··· 764 760 unsigned long data = atomic_long_read(&work->data); 765 761 766 762 if (data & WORK_STRUCT_PWQ) 767 - return ((struct pool_workqueue *) 768 - (data & WORK_STRUCT_WQ_DATA_MASK))->pool->id; 763 + return work_struct_pwq(data)->pool->id; 769 764 770 765 return data >> WORK_OFFQ_POOL_SHIFT; 771 766 }