Commit 274b6933 authored by Abhay Salunke's avatar Abhay Salunke Committed by Linus Torvalds

[PATCH] dell_rbu: Adding BIOS memory floor support

This patch has the changes to support the memory floor fix done in Dell
BIOS.  The BIOS incase of packet update mechanism would not accept packet
placed in memory below a cretain address.  This address is by default 128K
but can change.  The driver now can accept the memory floor if the user
chooses to make it will try to allocate contiguous physical memory above
the memory floor by allocating a set of packets till a valid memory
allocation is made.  All the allocates then are freed.  This repeats for
everty packet.

This patch was created by Michael E Brown and has been tested on 2.6.14-rc5
Signed-of-by: default avatarMichael E Brown <Michael_E_Brown@Dell.com>
Signed-off-by: default avatarAbhay Salunke <abhay_salunke@dell.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e2a8f7a1
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>");
MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION("3.0"); MODULE_VERSION("3.1");
#define BIOS_SCAN_LIMIT 0xffffffff #define BIOS_SCAN_LIMIT 0xffffffff
#define MAX_IMAGE_LENGTH 16 #define MAX_IMAGE_LENGTH 16
...@@ -73,6 +73,11 @@ module_param_string(image_type, image_type, sizeof (image_type), 0); ...@@ -73,6 +73,11 @@ module_param_string(image_type, image_type, sizeof (image_type), 0);
MODULE_PARM_DESC(image_type, MODULE_PARM_DESC(image_type,
"BIOS image type. choose- mono or packet or init"); "BIOS image type. choose- mono or packet or init");
static unsigned long allocation_floor = 0x100000;
module_param(allocation_floor, ulong, 0644);
MODULE_PARM_DESC(allocation_floor,
"Minimum address for allocations when using Packet mode");
struct packet_data { struct packet_data {
struct list_head list; struct list_head list;
size_t length; size_t length;
...@@ -99,61 +104,122 @@ static int create_packet(void *data, size_t length) ...@@ -99,61 +104,122 @@ static int create_packet(void *data, size_t length)
{ {
struct packet_data *newpacket; struct packet_data *newpacket;
int ordernum = 0; int ordernum = 0;
int retval = 0;
unsigned int packet_array_size = 0;
void **invalid_addr_packet_array = 0;
void *packet_data_temp_buf = 0;
unsigned int idx = 0;
pr_debug("create_packet: entry \n"); pr_debug("create_packet: entry \n");
if (!rbu_data.packetsize) { if (!rbu_data.packetsize) {
pr_debug("create_packet: packetsize not specified\n"); pr_debug("create_packet: packetsize not specified\n");
return -EINVAL; retval = -EINVAL;
goto out_noalloc;
} }
spin_unlock(&rbu_data.lock); spin_unlock(&rbu_data.lock);
newpacket = kmalloc(sizeof (struct packet_data), GFP_KERNEL);
spin_lock(&rbu_data.lock); newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL);
if (!newpacket) { if (!newpacket) {
printk(KERN_WARNING printk(KERN_WARNING
"dell_rbu:%s: failed to allocate new " "dell_rbu:%s: failed to allocate new "
"packet\n", __FUNCTION__); "packet\n", __FUNCTION__);
return -ENOMEM; retval = -ENOMEM;
spin_lock(&rbu_data.lock);
goto out_noalloc;
} }
ordernum = get_order(length); ordernum = get_order(length);
/* /*
* there is no upper limit on memory * BIOS errata mean we cannot allocate packets below 1MB or they will
* address for packetized mechanism * be overwritten by BIOS.
*
* array to temporarily hold packets
* that are below the allocation floor
*
* NOTE: very simplistic because we only need the floor to be at 1MB
* due to BIOS errata. This shouldn't be used for higher floors
* or you will run out of mem trying to allocate the array.
*/ */
spin_unlock(&rbu_data.lock); packet_array_size = max(
newpacket->data = (unsigned char *) __get_free_pages(GFP_KERNEL, (unsigned int)(allocation_floor / rbu_data.packetsize),
ordernum); (unsigned int)1);
spin_lock(&rbu_data.lock); invalid_addr_packet_array = kzalloc(packet_array_size * sizeof(void*),
GFP_KERNEL);
pr_debug("create_packet: newpacket %p\n", newpacket->data); if (!invalid_addr_packet_array) {
printk(KERN_WARNING
"dell_rbu:%s: failed to allocate "
"invalid_addr_packet_array \n",
__FUNCTION__);
retval = -ENOMEM;
spin_lock(&rbu_data.lock);
goto out_alloc_packet;
}
if (!newpacket->data) { while (!packet_data_temp_buf) {
packet_data_temp_buf = (unsigned char *)
__get_free_pages(GFP_KERNEL, ordernum);
if (!packet_data_temp_buf) {
printk(KERN_WARNING printk(KERN_WARNING
"dell_rbu:%s: failed to allocate new " "dell_rbu:%s: failed to allocate new "
"packet\n", __FUNCTION__); "packet\n", __FUNCTION__);
kfree(newpacket); retval = -ENOMEM;
return -ENOMEM; spin_lock(&rbu_data.lock);
goto out_alloc_packet_array;
} }
if ((unsigned long)virt_to_phys(packet_data_temp_buf)
< allocation_floor) {
pr_debug("packet 0x%lx below floor at 0x%lx.\n",
(unsigned long)virt_to_phys(
packet_data_temp_buf),
allocation_floor);
invalid_addr_packet_array[idx++] = packet_data_temp_buf;
packet_data_temp_buf = 0;
}
}
spin_lock(&rbu_data.lock);
newpacket->data = packet_data_temp_buf;
pr_debug("create_packet: newpacket at physical addr %lx\n",
(unsigned long)virt_to_phys(newpacket->data));
/* packets may not have fixed size */
newpacket->length = length;
newpacket->ordernum = ordernum; newpacket->ordernum = ordernum;
++rbu_data.num_packets; ++rbu_data.num_packets;
/*
* initialize the newly created packet headers /* initialize the newly created packet headers */
*/
INIT_LIST_HEAD(&newpacket->list); INIT_LIST_HEAD(&newpacket->list);
list_add_tail(&newpacket->list, &packet_data_head.list); list_add_tail(&newpacket->list, &packet_data_head.list);
/*
* packets may not have fixed size
*/
newpacket->length = length;
memcpy(newpacket->data, data, length); memcpy(newpacket->data, data, length);
pr_debug("create_packet: exit \n"); pr_debug("create_packet: exit \n");
return 0; out_alloc_packet_array:
/* always free packet array */
for (;idx>0;idx--) {
pr_debug("freeing unused packet below floor 0x%lx.\n",
(unsigned long)virt_to_phys(
invalid_addr_packet_array[idx-1]));
free_pages((unsigned long)invalid_addr_packet_array[idx-1],
ordernum);
}
kfree(invalid_addr_packet_array);
out_alloc_packet:
/* if error, free data */
if (retval)
kfree(newpacket);
out_noalloc:
return retval;
} }
static int packetize_data(void *data, size_t length) static int packetize_data(void *data, size_t length)
...@@ -693,3 +759,6 @@ static __exit void dcdrbu_exit(void) ...@@ -693,3 +759,6 @@ static __exit void dcdrbu_exit(void)
module_exit(dcdrbu_exit); module_exit(dcdrbu_exit);
module_init(dcdrbu_init); module_init(dcdrbu_init);
/* vim:noet:ts=8:sw=8
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment