|
@@ -1,6 +1,33 @@
|
|
|
#include <linux/export.h>
|
|
|
#include <linux/lockref.h>
|
|
|
|
|
|
+#ifdef CONFIG_CMPXCHG_LOCKREF
|
|
|
+
|
|
|
+/*
|
|
|
+ * Note that the "cmpxchg()" reloads the "old" value for the
|
|
|
+ * failure case.
|
|
|
+ */
|
|
|
+#define CMPXCHG_LOOP(CODE, SUCCESS) do { \
|
|
|
+ struct lockref old; \
|
|
|
+ BUILD_BUG_ON(sizeof(old) != 8); \
|
|
|
+ old.lock_count = ACCESS_ONCE(lockref->lock_count); \
|
|
|
+ while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \
|
|
|
+ struct lockref new = old, prev = old; \
|
|
|
+ CODE \
|
|
|
+ old.lock_count = cmpxchg(&lockref->lock_count, \
|
|
|
+ old.lock_count, new.lock_count); \
|
|
|
+ if (likely(old.lock_count == prev.lock_count)) { \
|
|
|
+ SUCCESS; \
|
|
|
+ } \
|
|
|
+ } \
|
|
|
+} while (0)
|
|
|
+
|
|
|
+#else
|
|
|
+
|
|
|
+#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
/**
|
|
|
* lockref_get - Increments reference count unconditionally
|
|
|
* @lockcnt: pointer to lockref structure
|
|
@@ -10,6 +37,12 @@
|
|
|
*/
|
|
|
void lockref_get(struct lockref *lockref)
|
|
|
{
|
|
|
+ CMPXCHG_LOOP(
|
|
|
+ new.count++;
|
|
|
+ ,
|
|
|
+ return;
|
|
|
+ );
|
|
|
+
|
|
|
spin_lock(&lockref->lock);
|
|
|
lockref->count++;
|
|
|
spin_unlock(&lockref->lock);
|
|
@@ -23,9 +56,18 @@ EXPORT_SYMBOL(lockref_get);
|
|
|
*/
|
|
|
int lockref_get_not_zero(struct lockref *lockref)
|
|
|
{
|
|
|
- int retval = 0;
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ CMPXCHG_LOOP(
|
|
|
+ new.count++;
|
|
|
+ if (!old.count)
|
|
|
+ return 0;
|
|
|
+ ,
|
|
|
+ return 1;
|
|
|
+ );
|
|
|
|
|
|
spin_lock(&lockref->lock);
|
|
|
+ retval = 0;
|
|
|
if (lockref->count) {
|
|
|
lockref->count++;
|
|
|
retval = 1;
|
|
@@ -43,6 +85,14 @@ EXPORT_SYMBOL(lockref_get_not_zero);
|
|
|
*/
|
|
|
int lockref_get_or_lock(struct lockref *lockref)
|
|
|
{
|
|
|
+ CMPXCHG_LOOP(
|
|
|
+ new.count++;
|
|
|
+ if (!old.count)
|
|
|
+ break;
|
|
|
+ ,
|
|
|
+ return 1;
|
|
|
+ );
|
|
|
+
|
|
|
spin_lock(&lockref->lock);
|
|
|
if (!lockref->count)
|
|
|
return 0;
|
|
@@ -59,6 +109,14 @@ EXPORT_SYMBOL(lockref_get_or_lock);
|
|
|
*/
|
|
|
int lockref_put_or_lock(struct lockref *lockref)
|
|
|
{
|
|
|
+ CMPXCHG_LOOP(
|
|
|
+ new.count--;
|
|
|
+ if (old.count <= 1)
|
|
|
+ break;
|
|
|
+ ,
|
|
|
+ return 1;
|
|
|
+ );
|
|
|
+
|
|
|
spin_lock(&lockref->lock);
|
|
|
if (lockref->count <= 1)
|
|
|
return 0;
|