@@ -392,6 +392,8 @@ do { \
392392
393393void __cmpxchg_user_key_called_with_bad_pointer (void );
394394
395+ #define CMPXCHG_USER_KEY_MAX_LOOPS 128
396+
395397static __always_inline int __cmpxchg_user_key (unsigned long address , void * uval ,
396398 __uint128_t old , __uint128_t new ,
397399 unsigned long key , int size )
@@ -401,6 +403,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
401403 switch (size ) {
402404 case 1 : {
403405 unsigned int prev , shift , mask , _old , _new ;
406+ unsigned long count ;
404407
405408 shift = (3 ^ (address & 3 )) << 3 ;
406409 address ^= address & 3 ;
@@ -410,6 +413,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
410413 asm volatile (
411414 " spka 0(%[key])\n"
412415 " sacf 256\n"
416+ " llill %[count],%[max_loops]\n"
413417 "0: l %[prev],%[address]\n"
414418 "1: nr %[prev],%[mask]\n"
415419 " xilf %[mask],0xffffffff\n"
@@ -421,7 +425,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
421425 " xr %[tmp],%[prev]\n"
422426 " xr %[new],%[tmp]\n"
423427 " nr %[tmp],%[mask]\n"
424- " jz 2b\n"
428+ " jnz 5f\n"
429+ " brct %[count],2b\n"
425430 "5: sacf 768\n"
426431 " spka %[default_key]\n"
427432 EX_TABLE_UA_LOAD_REG (0b , 5b , %[rc ], %[prev ])
@@ -433,15 +438,20 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
433438 [address ] "+ Q " (*(int *)address),
434439 [tmp ] "+ & d " (_old),
435440 [new ] "+ & d " (_new),
436- [mask ] "+ & d " (mask)
437- : [key ] "a" (key << 4 ),
438- [default_key ] "J" (PAGE_DEFAULT_KEY )
441+ [mask ] "+ & d " (mask),
442+ [count ] "= a " (count)
443+ : [key ] "%[count]" (key << 4 ),
444+ [default_key ] "J" (PAGE_DEFAULT_KEY ),
445+ [max_loops ] "J" (CMPXCHG_USER_KEY_MAX_LOOPS )
439446 : "memory ", " cc ");
440447 * (unsigned char * )uval = prev >> shift ;
448+ if (!count )
449+ rc = - EAGAIN ;
441450 return rc ;
442451 }
443452 case 2 : {
444453 unsigned int prev , shift , mask , _old , _new ;
454+ unsigned long count ;
445455
446456 shift = (2 ^ (address & 2 )) << 3 ;
447457 address ^= address & 2 ;
@@ -451,6 +461,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
451461 asm volatile (
452462 " spka 0(%[key])\n"
453463 " sacf 256\n"
464+ " llill %[count],%[max_loops]\n"
454465 "0: l %[prev],%[address]\n"
455466 "1: nr %[prev],%[mask]\n"
456467 " xilf %[mask],0xffffffff\n"
@@ -462,7 +473,8 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
462473 " xr %[tmp],%[prev]\n"
463474 " xr %[new],%[tmp]\n"
464475 " nr %[tmp],%[mask]\n"
465- " jz 2b\n"
476+ " jnz 5f\n"
477+ " brct %[count],2b\n"
466478 "5: sacf 768\n"
467479 " spka %[default_key]\n"
468480 EX_TABLE_UA_LOAD_REG (0b , 5b , %[rc ], %[prev ])
@@ -474,11 +486,15 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
474486 [address ] "+ Q " (*(int *)address),
475487 [tmp ] "+ & d " (_old),
476488 [new ] "+ & d " (_new),
477- [mask ] "+ & d " (mask)
478- : [key ] "a" (key << 4 ),
479- [default_key ] "J" (PAGE_DEFAULT_KEY )
489+ [mask ] "+ & d " (mask),
490+ [count ] "= a " (count)
491+ : [key ] "%[count]" (key << 4 ),
492+ [default_key ] "J" (PAGE_DEFAULT_KEY ),
493+ [max_loops ] "J" (CMPXCHG_USER_KEY_MAX_LOOPS )
480494 : "memory ", " cc ");
481495 * (unsigned short * )uval = prev >> shift ;
496+ if (!count )
497+ rc = - EAGAIN ;
482498 return rc ;
483499 }
484500 case 4 : {
@@ -568,6 +584,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
568584 *
569585 * Return: 0: cmpxchg executed
570586 * -EFAULT: an exception happened when trying to access *@ptr
587+ * -EAGAIN: maxed out number of retries (byte and short only)
571588 */
572589#define cmpxchg_user_key (ptr , uval , old , new , key ) \
573590({ \
0 commit comments