Char/Misc fixes for 4.15-rc3
Here are some small misc driver fixes for 4.15-rc3 to resolve reported issues. Specifically these are: - binder fix for a memory leak - vpd driver fixes for a number of reported problems - hyperv driver fix for memory accesses where it shouldn't be. All of these have been in linux-next for a while. There's also one more MAINTAINERS file update that came in today to get the Android developer's emails correct, which is also in this pull request, that was not in linux-next, but should not be an issue. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWia6IQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ykaKwCgx1lw4qMmetUzjKBX+OMgEkFZo3UAoIL/DLiB ZyMb52XWdP0pScSsFUyn =riSs -----END PGP SIGNATURE----- Merge tag 'char-misc-4.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc fixes from Greg KH: "Here are some small misc driver fixes for 4.15-rc3 to resolve reported issues. Specifically these are: - binder fix for a memory leak - vpd driver fixes for a number of reported problems - hyperv driver fix for memory accesses where it shouldn't be. All of these have been in linux-next for a while. There's also one more MAINTAINERS file update that came in today to get the Android developer's emails correct, which is also in this pull request, that was not in linux-next, but should not be an issue" * tag 'char-misc-4.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: MAINTAINERS: update Android driver maintainers. firmware: vpd: Fix platform driver and device registration/unregistration firmware: vpd: Tie firmware kobject to device lifetime firmware: vpd: Destroy vpd sections in remove function hv: kvp: Avoid reading past allocated blocks from KVP file Drivers: hv: vmbus: Fix a rescind issue ANDROID: binder: fix transaction leak.
This commit is contained in:
Коммит
6a5e05a47b
|
@ -859,7 +859,8 @@ F: kernel/configs/android*
|
||||||
ANDROID DRIVERS
|
ANDROID DRIVERS
|
||||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||||
M: Arve Hjønnevåg <arve@android.com>
|
M: Arve Hjønnevåg <arve@android.com>
|
||||||
M: Riley Andrews <riandrews@android.com>
|
M: Todd Kjos <tkjos@android.com>
|
||||||
|
M: Martijn Coenen <maco@android.com>
|
||||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
|
||||||
L: devel@driverdev.osuosl.org
|
L: devel@driverdev.osuosl.org
|
||||||
S: Supported
|
S: Supported
|
||||||
|
|
|
@ -1947,6 +1947,26 @@ static void binder_send_failed_reply(struct binder_transaction *t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binder_cleanup_transaction() - cleans up undelivered transaction
|
||||||
|
* @t: transaction that needs to be cleaned up
|
||||||
|
* @reason: reason the transaction wasn't delivered
|
||||||
|
* @error_code: error to return to caller (if synchronous call)
|
||||||
|
*/
|
||||||
|
static void binder_cleanup_transaction(struct binder_transaction *t,
|
||||||
|
const char *reason,
|
||||||
|
uint32_t error_code)
|
||||||
|
{
|
||||||
|
if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) {
|
||||||
|
binder_send_failed_reply(t, error_code);
|
||||||
|
} else {
|
||||||
|
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
||||||
|
"undelivered transaction %d, %s\n",
|
||||||
|
t->debug_id, reason);
|
||||||
|
binder_free_transaction(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* binder_validate_object() - checks for a valid metadata object in a buffer.
|
* binder_validate_object() - checks for a valid metadata object in a buffer.
|
||||||
* @buffer: binder_buffer that we're parsing.
|
* @buffer: binder_buffer that we're parsing.
|
||||||
|
@ -4015,12 +4035,20 @@ retry:
|
||||||
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
if (put_user(cmd, (uint32_t __user *)ptr)) {
|
||||||
if (t_from)
|
if (t_from)
|
||||||
binder_thread_dec_tmpref(t_from);
|
binder_thread_dec_tmpref(t_from);
|
||||||
|
|
||||||
|
binder_cleanup_transaction(t, "put_user failed",
|
||||||
|
BR_FAILED_REPLY);
|
||||||
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
ptr += sizeof(uint32_t);
|
ptr += sizeof(uint32_t);
|
||||||
if (copy_to_user(ptr, &tr, sizeof(tr))) {
|
if (copy_to_user(ptr, &tr, sizeof(tr))) {
|
||||||
if (t_from)
|
if (t_from)
|
||||||
binder_thread_dec_tmpref(t_from);
|
binder_thread_dec_tmpref(t_from);
|
||||||
|
|
||||||
|
binder_cleanup_transaction(t, "copy_to_user failed",
|
||||||
|
BR_FAILED_REPLY);
|
||||||
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
ptr += sizeof(tr);
|
ptr += sizeof(tr);
|
||||||
|
@ -4090,15 +4118,9 @@ static void binder_release_work(struct binder_proc *proc,
|
||||||
struct binder_transaction *t;
|
struct binder_transaction *t;
|
||||||
|
|
||||||
t = container_of(w, struct binder_transaction, work);
|
t = container_of(w, struct binder_transaction, work);
|
||||||
if (t->buffer->target_node &&
|
|
||||||
!(t->flags & TF_ONE_WAY)) {
|
binder_cleanup_transaction(t, "process died.",
|
||||||
binder_send_failed_reply(t, BR_DEAD_REPLY);
|
BR_DEAD_REPLY);
|
||||||
} else {
|
|
||||||
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
|
||||||
"undelivered transaction %d\n",
|
|
||||||
t->debug_id);
|
|
||||||
binder_free_transaction(t);
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case BINDER_WORK_RETURN_ERROR: {
|
case BINDER_WORK_RETURN_ERROR: {
|
||||||
struct binder_error *e = container_of(
|
struct binder_error *e = container_of(
|
||||||
|
|
|
@ -295,38 +295,60 @@ static int vpd_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return vpd_sections_init(entry.cbmem_addr);
|
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
|
||||||
|
if (!vpd_kobj)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = vpd_sections_init(entry.cbmem_addr);
|
||||||
|
if (ret) {
|
||||||
|
kobject_put(vpd_kobj);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vpd_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
vpd_section_destroy(&ro_vpd);
|
||||||
|
vpd_section_destroy(&rw_vpd);
|
||||||
|
|
||||||
|
kobject_put(vpd_kobj);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver vpd_driver = {
|
static struct platform_driver vpd_driver = {
|
||||||
.probe = vpd_probe,
|
.probe = vpd_probe,
|
||||||
|
.remove = vpd_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "vpd",
|
.name = "vpd",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct platform_device *vpd_pdev;
|
||||||
|
|
||||||
static int __init vpd_platform_init(void)
|
static int __init vpd_platform_init(void)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev;
|
int ret;
|
||||||
|
|
||||||
pdev = platform_device_register_simple("vpd", -1, NULL, 0);
|
ret = platform_driver_register(&vpd_driver);
|
||||||
if (IS_ERR(pdev))
|
if (ret)
|
||||||
return PTR_ERR(pdev);
|
return ret;
|
||||||
|
|
||||||
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
|
vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0);
|
||||||
if (!vpd_kobj)
|
if (IS_ERR(vpd_pdev)) {
|
||||||
return -ENOMEM;
|
platform_driver_unregister(&vpd_driver);
|
||||||
|
return PTR_ERR(vpd_pdev);
|
||||||
platform_driver_register(&vpd_driver);
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit vpd_platform_exit(void)
|
static void __exit vpd_platform_exit(void)
|
||||||
{
|
{
|
||||||
vpd_section_destroy(&ro_vpd);
|
platform_device_unregister(vpd_pdev);
|
||||||
vpd_section_destroy(&rw_vpd);
|
platform_driver_unregister(&vpd_driver);
|
||||||
kobject_put(vpd_kobj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(vpd_platform_init);
|
module_init(vpd_platform_init);
|
||||||
|
|
|
@ -659,22 +659,28 @@ void vmbus_close(struct vmbus_channel *channel)
|
||||||
*/
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mutex_lock(&vmbus_connection.channel_mutex);
|
|
||||||
/*
|
/*
|
||||||
* Close all the sub-channels first and then close the
|
* Close all the sub-channels first and then close the
|
||||||
* primary channel.
|
* primary channel.
|
||||||
*/
|
*/
|
||||||
list_for_each_safe(cur, tmp, &channel->sc_list) {
|
list_for_each_safe(cur, tmp, &channel->sc_list) {
|
||||||
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
||||||
vmbus_close_internal(cur_channel);
|
|
||||||
if (cur_channel->rescind) {
|
if (cur_channel->rescind) {
|
||||||
|
wait_for_completion(&cur_channel->rescind_event);
|
||||||
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
|
vmbus_close_internal(cur_channel);
|
||||||
hv_process_channel_removal(
|
hv_process_channel_removal(
|
||||||
cur_channel->offermsg.child_relid);
|
cur_channel->offermsg.child_relid);
|
||||||
|
} else {
|
||||||
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
|
vmbus_close_internal(cur_channel);
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Now close the primary.
|
* Now close the primary.
|
||||||
*/
|
*/
|
||||||
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
vmbus_close_internal(channel);
|
vmbus_close_internal(channel);
|
||||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,6 +333,7 @@ static struct vmbus_channel *alloc_channel(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
spin_lock_init(&channel->lock);
|
spin_lock_init(&channel->lock);
|
||||||
|
init_completion(&channel->rescind_event);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&channel->sc_list);
|
INIT_LIST_HEAD(&channel->sc_list);
|
||||||
INIT_LIST_HEAD(&channel->percpu_list);
|
INIT_LIST_HEAD(&channel->percpu_list);
|
||||||
|
@ -898,6 +899,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
/*
|
/*
|
||||||
* Now wait for offer handling to complete.
|
* Now wait for offer handling to complete.
|
||||||
*/
|
*/
|
||||||
|
vmbus_rescind_cleanup(channel);
|
||||||
while (READ_ONCE(channel->probe_done) == false) {
|
while (READ_ONCE(channel->probe_done) == false) {
|
||||||
/*
|
/*
|
||||||
* We wait here until any channel offer is currently
|
* We wait here until any channel offer is currently
|
||||||
|
@ -913,7 +915,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
if (channel->device_obj) {
|
if (channel->device_obj) {
|
||||||
if (channel->chn_rescind_callback) {
|
if (channel->chn_rescind_callback) {
|
||||||
channel->chn_rescind_callback(channel);
|
channel->chn_rescind_callback(channel);
|
||||||
vmbus_rescind_cleanup(channel);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -922,7 +923,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
*/
|
*/
|
||||||
dev = get_device(&channel->device_obj->device);
|
dev = get_device(&channel->device_obj->device);
|
||||||
if (dev) {
|
if (dev) {
|
||||||
vmbus_rescind_cleanup(channel);
|
|
||||||
vmbus_device_unregister(channel->device_obj);
|
vmbus_device_unregister(channel->device_obj);
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
|
@ -936,13 +936,14 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
* 2. Then close the primary channel.
|
* 2. Then close the primary channel.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&vmbus_connection.channel_mutex);
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
vmbus_rescind_cleanup(channel);
|
|
||||||
if (channel->state == CHANNEL_OPEN_STATE) {
|
if (channel->state == CHANNEL_OPEN_STATE) {
|
||||||
/*
|
/*
|
||||||
* The channel is currently not open;
|
* The channel is currently not open;
|
||||||
* it is safe for us to cleanup the channel.
|
* it is safe for us to cleanup the channel.
|
||||||
*/
|
*/
|
||||||
hv_process_channel_removal(rescind->child_relid);
|
hv_process_channel_removal(rescind->child_relid);
|
||||||
|
} else {
|
||||||
|
complete(&channel->rescind_event);
|
||||||
}
|
}
|
||||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -708,6 +708,7 @@ struct vmbus_channel {
|
||||||
u8 monitor_bit;
|
u8 monitor_bit;
|
||||||
|
|
||||||
bool rescind; /* got rescind msg */
|
bool rescind; /* got rescind msg */
|
||||||
|
struct completion rescind_event;
|
||||||
|
|
||||||
u32 ringbuffer_gpadlhandle;
|
u32 ringbuffer_gpadlhandle;
|
||||||
|
|
||||||
|
|
|
@ -193,11 +193,14 @@ static void kvp_update_mem_state(int pool)
|
||||||
for (;;) {
|
for (;;) {
|
||||||
readp = &record[records_read];
|
readp = &record[records_read];
|
||||||
records_read += fread(readp, sizeof(struct kvp_record),
|
records_read += fread(readp, sizeof(struct kvp_record),
|
||||||
ENTRIES_PER_BLOCK * num_blocks,
|
ENTRIES_PER_BLOCK * num_blocks - records_read,
|
||||||
filep);
|
filep);
|
||||||
|
|
||||||
if (ferror(filep)) {
|
if (ferror(filep)) {
|
||||||
syslog(LOG_ERR, "Failed to read file, pool: %d", pool);
|
syslog(LOG_ERR,
|
||||||
|
"Failed to read file, pool: %d; error: %d %s",
|
||||||
|
pool, errno, strerror(errno));
|
||||||
|
kvp_release_lock(pool);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +213,7 @@ static void kvp_update_mem_state(int pool)
|
||||||
|
|
||||||
if (record == NULL) {
|
if (record == NULL) {
|
||||||
syslog(LOG_ERR, "malloc failed");
|
syslog(LOG_ERR, "malloc failed");
|
||||||
|
kvp_release_lock(pool);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -224,15 +228,11 @@ static void kvp_update_mem_state(int pool)
|
||||||
fclose(filep);
|
fclose(filep);
|
||||||
kvp_release_lock(pool);
|
kvp_release_lock(pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvp_file_init(void)
|
static int kvp_file_init(void)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
FILE *filep;
|
|
||||||
size_t records_read;
|
|
||||||
char *fname;
|
char *fname;
|
||||||
struct kvp_record *record;
|
|
||||||
struct kvp_record *readp;
|
|
||||||
int num_blocks;
|
|
||||||
int i;
|
int i;
|
||||||
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
|
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
|
||||||
|
|
||||||
|
@ -246,61 +246,19 @@ static int kvp_file_init(void)
|
||||||
|
|
||||||
for (i = 0; i < KVP_POOL_COUNT; i++) {
|
for (i = 0; i < KVP_POOL_COUNT; i++) {
|
||||||
fname = kvp_file_info[i].fname;
|
fname = kvp_file_info[i].fname;
|
||||||
records_read = 0;
|
|
||||||
num_blocks = 1;
|
|
||||||
sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
|
sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
|
||||||
fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
|
fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
|
||||||
|
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
||||||
filep = fopen(fname, "re");
|
|
||||||
if (!filep) {
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
record = malloc(alloc_unit * num_blocks);
|
|
||||||
if (record == NULL) {
|
|
||||||
fclose(filep);
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
for (;;) {
|
|
||||||
readp = &record[records_read];
|
|
||||||
records_read += fread(readp, sizeof(struct kvp_record),
|
|
||||||
ENTRIES_PER_BLOCK,
|
|
||||||
filep);
|
|
||||||
|
|
||||||
if (ferror(filep)) {
|
|
||||||
syslog(LOG_ERR, "Failed to read file, pool: %d",
|
|
||||||
i);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!feof(filep)) {
|
|
||||||
/*
|
|
||||||
* We have more data to read.
|
|
||||||
*/
|
|
||||||
num_blocks++;
|
|
||||||
record = realloc(record, alloc_unit *
|
|
||||||
num_blocks);
|
|
||||||
if (record == NULL) {
|
|
||||||
fclose(filep);
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
kvp_file_info[i].fd = fd;
|
kvp_file_info[i].fd = fd;
|
||||||
kvp_file_info[i].num_blocks = num_blocks;
|
kvp_file_info[i].num_blocks = 1;
|
||||||
kvp_file_info[i].records = record;
|
kvp_file_info[i].records = malloc(alloc_unit);
|
||||||
kvp_file_info[i].num_records = records_read;
|
if (kvp_file_info[i].records == NULL)
|
||||||
fclose(filep);
|
return 1;
|
||||||
|
kvp_file_info[i].num_records = 0;
|
||||||
|
kvp_update_mem_state(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче