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

[MIPS] RTLX: Harden against compiler reordering and optimization.

RTLX communication is based on lock-free shared memory buffers. It
happened to be working by luck so far but relies on the optimizer doing
certain optimizations but no reordering.

Fixed by inserting proper barriers in rtlx_read and rtlx_write, and careful
pointer dereferencing.

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

+24 -17
+24 -17
arch/mips/kernel/rtlx.c
··· 306 306 307 307 ssize_t rtlx_read(int index, void *buff, size_t count, int user) 308 308 { 309 - size_t fl = 0L; 309 + size_t lx_write, fl = 0L; 310 310 struct rtlx_channel *lx; 311 311 312 312 if (rtlx == NULL) ··· 314 314 315 315 lx = &rtlx->channel[index]; 316 316 317 + smp_rmb(); 318 + lx_write = lx->lx_write; 319 + 317 320 /* find out how much in total */ 318 321 count = min(count, 319 - (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) 322 + (size_t)(lx_write + lx->buffer_size - lx->lx_read) 320 323 % lx->buffer_size); 321 324 322 325 /* then how much from the read pointer onwards */ 323 - fl = min( count, (size_t)lx->buffer_size - lx->lx_read); 326 + fl = min(count, (size_t)lx->buffer_size - lx->lx_read); 324 327 325 - copy_to(buff, &lx->lx_buffer[lx->lx_read], fl, user); 328 + copy_to(buff, lx->lx_buffer + lx->lx_read, fl, user); 326 329 327 330 /* and if there is anything left at the beginning of the buffer */ 328 - if ( count - fl ) 329 - copy_to (buff + fl, lx->lx_buffer, count - fl, user); 331 + if (count - fl) 332 + copy_to(buff + fl, lx->lx_buffer, count - fl, user); 330 333 331 - /* update the index */ 332 - lx->lx_read += count; 333 - lx->lx_read %= lx->buffer_size; 334 + smp_wmb(); 335 + lx->lx_read = (lx->lx_read + count) % lx->buffer_size; 336 + smp_wmb(); 334 337 335 338 return count; 336 339 } ··· 341 338 ssize_t rtlx_write(int index, void *buffer, size_t count, int user) 342 339 { 343 340 struct rtlx_channel *rt; 341 + size_t rt_read; 344 342 size_t fl; 345 343 346 344 if (rtlx == NULL) ··· 349 345 350 346 rt = &rtlx->channel[index]; 351 347 348 + smp_rmb(); 349 + rt_read = rt->rt_read; 350 + 352 351 /* total number of bytes to copy */ 353 352 count = min(count, 354 - (size_t)write_spacefree(rt->rt_read, rt->rt_write, 355 - rt->buffer_size)); 353 + (size_t)write_spacefree(rt_read, rt->rt_write, rt->buffer_size)); 356 354 357 355 /* first bit from write pointer to the end of the buffer, or count */ 358 356 fl = min(count, (size_t) rt->buffer_size - rt->rt_write); 359 357 360 - copy_from (&rt->rt_buffer[rt->rt_write], buffer, fl, user); 358 + copy_from(rt->rt_buffer + rt->rt_write, buffer, fl, user); 361 359 362 360 /* if there's any left copy to the beginning of the buffer */ 363 - if( count - fl ) 364 - copy_from (rt->rt_buffer, buffer + fl, count - fl, user); 361 + if (count - fl) 362 + copy_from(rt->rt_buffer, buffer + fl, count - fl, user); 365 363 366 - rt->rt_write += count; 367 - rt->rt_write %= rt->buffer_size; 364 + smp_wmb(); 365 + rt->rt_write = (rt->rt_write + count) % rt->buffer_size; 366 + smp_wmb(); 368 367 369 - return(count); 368 + return count; 370 369 } 371 370 372 371