VMCI: Use 32bit atomics for queue headers on X86_32

This change restricts the reading and setting of the head and tail
pointers on 32bit X86 to 32bit for both correctness and
performance reasons. On uniprocessor X86_32, the atomic64_read
may be implemented as a non-locked cmpxchg8b. This may result in
updates to the pointers done by the VMCI device being overwritten.
On MP systems, there is no such correctness issue, but using 32bit
atomics avoids the overhead of the locked 64bit operation. All this
is safe because the queue size on 32bit systems will never exceed
a 32bit value.

Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Jorgen Hansen <jhansen@vmware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Jorgen Hansen 2015-11-12 01:29:32 -08:00 коммит произвёл Greg Kroah-Hartman
Родитель fe760e4d64
Коммит f42a0fd13b
2 изменённых файлов: 40 добавлений и 5 удалений

Просмотреть файл

@ -113,5 +113,5 @@ module_exit(vmci_drv_exit);
MODULE_AUTHOR("VMware, Inc."); MODULE_AUTHOR("VMware, Inc.");
MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
MODULE_VERSION("1.1.3.0-k"); MODULE_VERSION("1.1.4.0-k");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -733,6 +733,41 @@ static inline void *vmci_event_data_payload(struct vmci_event_data *ev_data)
return (void *)vmci_event_data_const_payload(ev_data); return (void *)vmci_event_data_const_payload(ev_data);
} }
/*
* Helper to read a value from a head or tail pointer. For X86_32, the
* pointer is treated as a 32bit value, since the pointer value
* never exceeds a 32bit value in this case. Also, doing an
* atomic64_read on X86_32 uniprocessor systems may be implemented
* as a non locked cmpxchg8b, that may end up overwriting updates done
* by the VMCI device to the memory location. On 32bit SMP, the lock
* prefix will be used, so correctness isn't an issue, but using a
* 64bit operation still adds unnecessary overhead.
*/
static inline u64 vmci_q_read_pointer(atomic64_t *var)
{
#if defined(CONFIG_X86_32)
return atomic_read((atomic_t *)var);
#else
return atomic64_read(var);
#endif
}
/*
* Helper to set the value of a head or tail pointer. For X86_32, the
* pointer is treated as a 32bit value, since the pointer value
* never exceeds a 32bit value in this case. On 32bit SMP, using a
* locked cmpxchg8b adds unnecessary overhead.
*/
static inline void vmci_q_set_pointer(atomic64_t *var,
u64 new_val)
{
#if defined(CONFIG_X86_32)
return atomic_set((atomic_t *)var, (u32)new_val);
#else
return atomic64_set(var, new_val);
#endif
}
/* /*
* Helper to add a given offset to a head or tail pointer. Wraps the * Helper to add a given offset to a head or tail pointer. Wraps the
* value of the pointer around the max size of the queue. * value of the pointer around the max size of the queue.
@ -741,14 +776,14 @@ static inline void vmci_qp_add_pointer(atomic64_t *var,
size_t add, size_t add,
u64 size) u64 size)
{ {
u64 new_val = atomic64_read(var); u64 new_val = vmci_q_read_pointer(var);
if (new_val >= size - add) if (new_val >= size - add)
new_val -= size; new_val -= size;
new_val += add; new_val += add;
atomic64_set(var, new_val); vmci_q_set_pointer(var, new_val);
} }
/* /*
@ -758,7 +793,7 @@ static inline u64
vmci_q_header_producer_tail(const struct vmci_queue_header *q_header) vmci_q_header_producer_tail(const struct vmci_queue_header *q_header)
{ {
struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header;
return atomic64_read(&qh->producer_tail); return vmci_q_read_pointer(&qh->producer_tail);
} }
/* /*
@ -768,7 +803,7 @@ static inline u64
vmci_q_header_consumer_head(const struct vmci_queue_header *q_header) vmci_q_header_consumer_head(const struct vmci_queue_header *q_header)
{ {
struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header; struct vmci_queue_header *qh = (struct vmci_queue_header *)q_header;
return atomic64_read(&qh->consumer_head); return vmci_q_read_pointer(&qh->consumer_head);
} }
/* /*