mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-25 05:34:00 +08:00
fsi: core: Fix small accesses and unaligned offsets via sysfs
Subtracting the offset delta from four-byte alignment lead to wrapping of the requested length where `count` is less than `off`. Generalise the length handling to enable and optimise aligned access sizes for all offset and size combinations. The new formula produces the following results for given offset and count values: offset count | length --------------+------- 0 1 | 1 0 2 | 2 0 3 | 2 0 4 | 4 0 5 | 4 1 1 | 1 1 2 | 1 1 3 | 1 1 4 | 1 1 5 | 1 2 1 | 1 2 2 | 2 2 3 | 2 2 4 | 2 2 5 | 2 3 1 | 1 3 2 | 1 3 3 | 1 3 4 | 1 3 5 | 1 We might need something like this for the cfam chardevs as well, for example we don't currently implement any alignment restrictions / handling in the hardware master driver. Signed-off-by: Andrew Jeffery <andrew@aj.id.au> Signed-off-by: Joel Stanley <joel@jms.id.au> Link: https://lore.kernel.org/r/20191108051945.7109-6-joel@jms.id.au Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
ae77481601
commit
9f4c2b516b
@ -544,6 +544,31 @@ static int fsi_slave_scan(struct fsi_slave *slave)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long aligned_access_size(size_t offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long offset_unit, count_unit;
|
||||||
|
|
||||||
|
/* Criteria:
|
||||||
|
*
|
||||||
|
* 1. Access size must be less than or equal to the maximum access
|
||||||
|
* width or the highest power-of-two factor of offset
|
||||||
|
* 2. Access size must be less than or equal to the amount specified by
|
||||||
|
* count
|
||||||
|
*
|
||||||
|
* The access width is optimal if we can calculate 1 to be strictly
|
||||||
|
* equal while still satisfying 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Find 1 by the bottom bit of offset (with a 4 byte access cap) */
|
||||||
|
offset_unit = BIT(__builtin_ctzl(offset | 4));
|
||||||
|
|
||||||
|
/* Find 2 by the top bit of count */
|
||||||
|
count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count));
|
||||||
|
|
||||||
|
/* Constrain the maximum access width to the minimum of both criteria */
|
||||||
|
return BIT(__builtin_ctzl(offset_unit | count_unit));
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
|
static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
|
||||||
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
struct kobject *kobj, struct bin_attribute *attr, char *buf,
|
||||||
loff_t off, size_t count)
|
loff_t off, size_t count)
|
||||||
@ -559,8 +584,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (total_len = 0; total_len < count; total_len += read_len) {
|
for (total_len = 0; total_len < count; total_len += read_len) {
|
||||||
read_len = min_t(size_t, count, 4);
|
read_len = aligned_access_size(off, count - total_len);
|
||||||
read_len -= off & 0x3;
|
|
||||||
|
|
||||||
rc = fsi_slave_read(slave, off, buf + total_len, read_len);
|
rc = fsi_slave_read(slave, off, buf + total_len, read_len);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -587,8 +611,7 @@ static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (total_len = 0; total_len < count; total_len += write_len) {
|
for (total_len = 0; total_len < count; total_len += write_len) {
|
||||||
write_len = min_t(size_t, count, 4);
|
write_len = aligned_access_size(off, count - total_len);
|
||||||
write_len -= off & 0x3;
|
|
||||||
|
|
||||||
rc = fsi_slave_write(slave, off, buf + total_len, write_len);
|
rc = fsi_slave_write(slave, off, buf + total_len, write_len);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
Loading…
Reference in New Issue
Block a user