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

[media] media: dvb_ringbuffer: Add memory barriers

Implement memory barriers according to Documentation/circular-buffers.txt:
- use smp_store_release() to update ringbuffer read/write pointers
- use smp_load_acquire() to load write pointer on reader side
- use ACCESS_ONCE() to load read pointer on writer side

This fixes data stream corruptions observed e.g. on an ARM Cortex-A9
quad core system with different types (PCI, USB) of DVB tuners.

Signed-off-by: Soeren Moch <smoch@web.de>
Cc: stable@vger.kernel.org # 3.14+
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>

authored by

Soeren Moch and committed by
Mauro Carvalho Chehab
ca6e6126 65ad26cd

+61 -13
+61 -13
drivers/media/dvb-core/dvb_ringbuffer.c
··· 55 55 56 56 int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) 57 57 { 58 - return (rbuf->pread==rbuf->pwrite); 58 + /* smp_load_acquire() to load write pointer on reader side 59 + * this pairs with smp_store_release() in dvb_ringbuffer_write(), 60 + * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 61 + * 62 + * for memory barriers also see Documentation/circular-buffers.txt 63 + */ 64 + return (rbuf->pread == smp_load_acquire(&rbuf->pwrite)); 59 65 } 60 66 61 67 ··· 70 64 { 71 65 ssize_t free; 72 66 73 - free = rbuf->pread - rbuf->pwrite; 67 + /* ACCESS_ONCE() to load read pointer on writer side 68 + * this pairs with smp_store_release() in dvb_ringbuffer_read(), 69 + * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(), 70 + * or dvb_ringbuffer_reset() 71 + */ 72 + free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite; 74 73 if (free <= 0) 75 74 free += rbuf->size; 76 75 return free-1; ··· 87 76 { 88 77 ssize_t avail; 89 78 90 - avail = rbuf->pwrite - rbuf->pread; 79 + /* smp_load_acquire() to load write pointer on reader side 80 + * this pairs with smp_store_release() in dvb_ringbuffer_write(), 81 + * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 82 + */ 83 + avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread; 91 84 if (avail < 0) 92 85 avail += rbuf->size; 93 86 return avail; ··· 101 86 102 87 void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) 103 88 { 104 - rbuf->pread = rbuf->pwrite; 89 + /* dvb_ringbuffer_flush() counts as read operation 90 + * smp_load_acquire() to load write pointer 91 + * smp_store_release() to update read pointer, this ensures that the 92 + * correct pointer is visible for subsequent dvb_ringbuffer_free() 93 + * calls on other cpu cores 94 + */ 95 + smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite)); 105 96 rbuf->error = 0; 106 97 } 107 98 EXPORT_SYMBOL(dvb_ringbuffer_flush); 108 99 109 100 void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) 110 101 { 111 - rbuf->pread = rbuf->pwrite = 0; 102 + /* dvb_ringbuffer_reset() counts as read and write operation 103 + * smp_store_release() to update read pointer 104 + */ 105 + smp_store_release(&rbuf->pread, 0); 106 + /* smp_store_release() to update write pointer */ 107 + smp_store_release(&rbuf->pwrite, 0); 112 108 rbuf->error = 0; 113 109 } 114 110 ··· 145 119 return -EFAULT; 146 120 buf += split; 147 121 todo -= split; 148 - rbuf->pread = 0; 122 + /* smp_store_release() for read pointer update to ensure 123 + * that buf is not overwritten until read is complete, 124 + * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() 125 + */ 126 + smp_store_release(&rbuf->pread, 0); 149 127 } 150 128 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) 151 129 return -EFAULT; 152 130 153 - rbuf->pread = (rbuf->pread + todo) % rbuf->size; 131 + /* smp_store_release() to update read pointer, see above */ 132 + smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 154 133 155 134 return len; 156 135 } ··· 170 139 memcpy(buf, rbuf->data+rbuf->pread, split); 171 140 buf += split; 172 141 todo -= split; 173 - rbuf->pread = 0; 142 + /* smp_store_release() for read pointer update to ensure 143 + * that buf is not overwritten until read is complete, 144 + * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() 145 + */ 146 + smp_store_release(&rbuf->pread, 0); 174 147 } 175 148 memcpy(buf, rbuf->data+rbuf->pread, todo); 176 149 177 - rbuf->pread = (rbuf->pread + todo) % rbuf->size; 150 + /* smp_store_release() to update read pointer, see above */ 151 + smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 178 152 } 179 153 180 154 ··· 194 158 memcpy(rbuf->data+rbuf->pwrite, buf, split); 195 159 buf += split; 196 160 todo -= split; 197 - rbuf->pwrite = 0; 161 + /* smp_store_release() for write pointer update to ensure that 162 + * written data is visible on other cpu cores before the pointer 163 + * update, this pairs with smp_load_acquire() in 164 + * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 165 + */ 166 + smp_store_release(&rbuf->pwrite, 0); 198 167 } 199 168 memcpy(rbuf->data+rbuf->pwrite, buf, todo); 200 - rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; 169 + /* smp_store_release() for write pointer update, see above */ 170 + smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 201 171 202 172 return len; 203 173 } ··· 223 181 return len - todo; 224 182 buf += split; 225 183 todo -= split; 226 - rbuf->pwrite = 0; 184 + /* smp_store_release() for write pointer update to ensure that 185 + * written data is visible on other cpu cores before the pointer 186 + * update, this pairs with smp_load_acquire() in 187 + * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 188 + */ 189 + smp_store_release(&rbuf->pwrite, 0); 227 190 } 228 191 status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); 229 192 if (status) 230 193 return len - todo; 231 - rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; 194 + /* smp_store_release() for write pointer update, see above */ 195 + smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 232 196 233 197 return len; 234 198 }