Abhay Salunke <[email protected]> wrote:
>
> The dell_rbu driver is required for updating BIOS on DELL servers and client
> systems. The driver lets a user application download the BIOS image in to
> contiguous physical memory pages; the driver exposes the memory via sysfs
> filesystem. The update mechanism basically has two approcahes; one by
> allocating contiguous physical memory and the second approach is by allocating
> small chunks of contiguous physical memory.
>
> The patch file is attached to this mail.
Greg, could you please review the sysfs usage?
> --- linux-2.6.11.8.ORIG/Documentation/DELL_RBU.txt 1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.11.8/Documentation/DELL_RBU.txt 2005-05-09 16:34:47.477703152 -0500
2.6.11.8 is very old in kernel terms. It's OK for this patch because it's
pretty standalone. But in general, please raise patches against
contemporary kernels. 2.6.12-rc4 would be appropriate at present.
> +
> +#define BIT(x) (1UL << ((x)%BITS_PER_LONG))
This is used only for:
+ rbu_data.image_update_buffer_size = BIT(ordernum) * PAGE_SIZE;
Please remove BIT() and just do
+ rbu_data.image_update_buffer_size = PAGE_SIZE << ordernum;
> +
> +static struct {
Some gcc's don't like anonymous structs (I think?). Still, it would be
more conventional to give this guy a name.
> + void *image_update_buffer;
> + unsigned long image_update_buffer_size;
> + unsigned long bios_image_size;
> + unsigned long image_update_order_number;
> + spinlock_t lock;
> + unsigned long packet_read_count;
> + unsigned long packet_write_count;
> + unsigned long num_packets;
> + unsigned long packetsize;
> +}rbu_data ;
Make this
} rbu_data;
> +struct packet_data{
struct packet_data {
> + struct list_head list;
> + size_t length;
> + void *rbu_data;
Should that be
struct rbu_data *rbu_data;
?
> +
> +static decl_subsys(rbu,&ktype_rbu,NULL);
> +
static decl_subsys(rbu, &ktype_rbu, NULL);
> +static void init_rbu_lock(void *plock)
> +{
> + spin_lock_init((spinlock_t *)plock);
> +}
> +
> +static void lock_rbu_access(void *plock)
> +{
> + spin_lock((spinlock_t *)plock);
> +}
> +
> +static void unlock_rbu_access(void *plock)
> +{
> + spin_unlock((spinlock_t *)plock);
> +}
Remove these and simply open-code the spinlock functions.
> +void init_packet_head(void)
> +{
> + INIT_LIST_HEAD((struct list_head *)&packet_data_head);
OK, in lots of places the patch typecasts packet_data_head to a list_head,
then does list_head operations on it, relying on the fact that
packet_data.list is the first element. Very weird.
Please replace all those with (for example)
INIT_LIST_HEAD(&packet_data_head.list);
> +static int fill_last_packet(void *data, size_t length)
> +{
> + struct list_head *ptemp_list;
> + struct packet_data *ppacket = NULL;
> + int packet_count = 0;
> +
> + pr_debug("fill_last_packet: entry \n");
> +
> + /* check if we have any packets */
> + if (0 == rbu_data.num_packets){
I know that some people like the `if (constant == variable)' thing to pick
up "= instead of ==" typos. But gcc does warn about that mistake anyway,
and I think most people find the above construct to be artificial, and
harder to read. Would prefer that all these be swapped around please.
Would also prefer a space before the '{'!
> + ptemp_list = ((struct list_head *)&packet_data_head)->next;
Use packet_head_data.list
> +
> + while(--packet_count)
> + {
while (--packet_count) {
> + if ((rbu_data.packet_write_count + length) > rbu_data.packetsize) {
^^
> + printk(KERN_WARNING"fill_last_packet: packet size data overrun\n");
^ space here
> +*/
> +static void *get_free_pages_limited(unsigned long size,
> + int *ordernum,
> + unsigned long limit)
Wild whitespace here?
> +{
> + unsigned long ImgBufPhysAddr;
Please stick with lower case and underscores in identifiers.
> + void *pbuf = NULL;
> +
> + *ordernum = get_order(size);
> + /* check if we are not getting a very large file
> + This can happen as a user error in entering the file size */
The comment layout is a bit odd.
/*
* Check if we are not getting a very large file. This can happen
* as a user error in entering the file size.
*/
> + if (*ordernum == BITS_PER_LONG){
^ space
> + /* The incoming size is very large */
> + pr_debug("get_free_pages_limited: Incoming size is very large\n");
> + return NULL;
> + }
> +
> + /* try allocating a new buffer to fit the request */
> + pbuf =(unsigned char *)__get_free_pages(GFP_KERNEL, *ordernum);
> +
> + if (pbuf != NULL) {
> + /* check if the image is with in limits */
> + ImgBufPhysAddr = (unsigned long)virt_to_phys((void *)pbuf);
> +
> + if ((limit != 0) &&
> + ((ImgBufPhysAddr + size) > limit)) {
> +
> + pr_debug("Got memory above 4GB range, free this and try with DMA memory\n");
The indenting has gone funny here.
> +
> + /* free this memory as we need it with in 4GB range */
> + free_pages ((unsigned long)pbuf, *ordernum);
> +
> + /* try allocating a new buffer from the GFP_DMA range
> + as it is with in 16MB range.*/
> + pbuf =(unsigned char *)__get_free_pages(GFP_DMA, *ordernum);
> +
> + if (pbuf == NULL)
> + pr_debug("Failed to get memory of size %ld using GFP_DMA\n", size);
> + }
> + }
> + return pbuf;
> +}
What architecture is this code designed for? On x86 a GFP_KERNEL
allocation will never return highmem. I guess x86_64?
I assume this code is here because the x86_64 BIOS will only access the
lower 4GB? If so, a comment to that extent would be useful.
Sometime I expect that x86_64 will gain a new zone, GFP_DMA32 which will be
guaranteed to return memory below he 4GB point. When that happens, this
driver should be converted to use it.
> +static int create_packet(size_t length)
> +{
> + struct packet_data *newpacket;
> + int ordernum =0;
^ space
> + newpacket = kmalloc(sizeof(struct packet_data) , GFP_KERNEL);
^
> + if(newpacket == NULL){
^ ^ spaces
> + if(newpacket->rbu_data == NULL){
Ditto. Lots of places.
> + printk(KERN_WARNING"create_packet: failed to allocate new packet\n");
> + return -ENOMEM;
> + }
> +
> + newpacket->ordernum = ordernum;
> +
> + ++rbu_data.num_packets;
> + /* initialize the newly created packet headers */
> + INIT_LIST_HEAD(&newpacket->list);
> + /* add this packet to the link list */
> + list_add_tail(&newpacket->list, (struct list_head *)&packet_data_head);
Does this list not need locking?
Use packet_data_head.list, remove the typecast.
> + /* packets are of fixed sizes so initialize the length to rbu_data.packetsize*/
Try to fit the code into an 80-column xterm, please.
> +/*
> + do_packet_read :
> + This is a helper function which reads the packet data of the
> + current list.
> + data: is the incoming buffer
> + ptemp_list: points to the incoming list item
> + length: is the length of the free space in the buffer.
> + bytes_read: is the total number of bytes read already from
> + the packet list
> + list_read_count: is the counter to keep track of the number
> + of bytes read out of each packet.
> +*/
> +int do_packet_read(char * data, struct list_head *ptemp_list, int length,
It's (a bit) more conventional to not have the space after the `*' when
declaring pointers:
char *data
Here, both styles are used in the same line!
> +{
> + void *ptemp_buf;
> + struct packet_data *newpacket = NULL;
This initialisation is unneeded.
> + int bytes_copied = 0;
> + void *pDest = data;
lowercase and underscore in identifiers
> + int j = 0;
> +
> + newpacket = list_entry(ptemp_list,struct packet_data, list);
> + *list_read_count += newpacket->length;
> +
> + if (*list_read_count > bytes_read) {
> + /* point to the start of unread data */
> + j = newpacket->length - (*list_read_count - bytes_read);
> + /* point to the offset in the packet buffer*/
> + ptemp_buf = (u8 *)newpacket->rbu_data + j;
The cast is actually unneeded - gcc will treat void* as a "pointer to
something which has sizeof==1" when performing such arithmetic. I believe
this is a gcc extension.
> + /* check if there is enough room in the
> + incoming buffer*/
Odd comment layout.
> +/*
> + packet_read_list:
> + This function reads the data out of the packet link list.
> + It will read data from multiple packets depending upon the
> + size of the incoming buffer.
> + data: is the incoming buffer pointer
> + *pread_length: is the length of the incoming buffer. At return
> + this value is adjusted to the actual size of the data read.
> +*/
> +static int packet_read_list(char *data, size_t *pread_length)
> +{
> + struct list_head *ptemp_list;
> + int temp_count = 0;
> + int bytes_copied = 0;
> + int bytes_read = 0;
> + int remaining_bytes =0;
> + char *pDest = data;
Are all these initialisations actually needed?
> + ptemp_list = ((struct list_head *)&packet_data_head)->next;
packet_data_head.next
> + while(!list_empty(ptemp_list))
> + {
while (!list_empty(ptemp_list)) {
> +static void packet_empty_list(void)
> +{
> + struct list_head *ptemp_list;
> + struct list_head *pnext_list;
> + struct packet_data *newpacket;
> +
> + ptemp_list = ((struct list_head *)&packet_data_head)->next;
ditto
> + while(!list_empty(ptemp_list))
> + {
ditto
I think list_for_each_entry_safe() would work here.
> +/*
> + img_update_free:
> + Frees the buffer allocated for storing BIOS image
> + Always called with lock held
> +*/
> +static void img_update_free( void)
static void img_update_free(void)
> +{
> + if (rbu_data.image_update_buffer == NULL)
> + return;
Can this happen?
> + /* zero out this buffer before freeing it */
> + memset(rbu_data.image_update_buffer, 0, rbu_data.image_update_buffer_size);
> + /* unlock as free_pages might sleep */
> + unlock_rbu_access(&rbu_data.lock);
> + free_pages((unsigned long)rbu_data.image_update_buffer,
> + rbu_data.image_update_order_number);
> + lock_rbu_access(&rbu_data.lock);
free_pages() will never sleep.
> + rbu_data.image_update_buffer = NULL;
> + rbu_data.image_update_buffer_size = 0;
> + rbu_data.bios_image_size = 0;
> +}
Are these assignments needed?
> +static int img_update_realloc(unsigned long size)
> +{
> + unsigned char *image_update_buffer = NULL;
> + unsigned long rc;
> + int ordernum =0;
> +
> +
> + /* check if the buffer of sufficient size has been already allocated */
> + if (rbu_data.image_update_buffer_size >= size) {
> + /* check for corruption */
> + if ((size != 0) && (rbu_data.image_update_buffer == NULL)) {
> + pr_debug("img_update_realloc: corruption check failed\n");
> + return -ENOMEM;
ENOMEM seems to be the wrong error to return here.
> + }
> + /* we have a valid pre-allocated buffer with sufficient size */
> + return 0;
> + }
> +
> + /* free any previously allocated buffer */
> + img_update_free();
> +
> + /* This has already been called as locked so we can now unlock
> + and proceed to calling get_free_pages_limited as this function
> + can sleep*/
> + unlock_rbu_access(&rbu_data.lock);
> +
> + image_update_buffer = (unsigned char *)get_free_pages_limited(size,
get_free_pages_limited() returns void*, so no typecast is needed.
> + &ordernum,
> + BIOS_SCAN_LIMIT);
> +
> + /* acquire the semaphore again */
It's actually a spinlock, not a semaphore.
> + lock_rbu_access(&rbu_data.lock);
> +
> + if (image_update_buffer != NULL) {
> + /* store address for the new buffer */
> + rbu_data.image_update_buffer = image_update_buffer;
> + /* adjust allocated size */
> + rbu_data.image_update_buffer_size = BIT(ordernum) * PAGE_SIZE;
> + /* save the current order number */
> + rbu_data.image_update_order_number = ordernum;
> + /* initialize the new buffer data to 0 */
> + memset(rbu_data.image_update_buffer,0, rbu_data.image_update_buffer_size);
> + pr_debug("img_update_realloc: success\n");
> + /* success */
> + rc = 0;
> + } else {
> + pr_debug("Not enough memory for image update:order number = %d"
> + ",size = %ld\n",ordernum, size);
> + rc = -ENOMEM;
> + }
> +
> + return rc;
> +} /* img_update_realloc */
The comment over img_update_realloc() should mention that it returns with
the lock held.
> +
> +/*
> + read_packet_data_size:
> + Returns the size of an RBU packet; if no packets present returns 0
> +*/
> +static ssize_t read_packet_data_size(struct kobject *kobj, char *buffer,
> + loff_t pos, size_t count)
> +{
> + unsigned int size = 0;
> + if (pos == 0) {
> + lock_rbu_access(&rbu_data.lock);
> + size = sprintf(buffer, "%lu\n", rbu_data.packetsize);
> + unlock_rbu_access(&rbu_data.lock);
The locking here is meaningless. rbu_data.packetsize can change any time
before or after the read.
> + }
> + return size;
> +}
> +
> +/*
> + write_packet_data_size:
> + Writes the RBU data size supplied by the user, if the
> + data size supplied is non zero number, this function
> + records the packet size for any packet allocations.
> + If a byte size of zero is supplied this function will free
> + the previously allocated packets.
> +*/
> +static ssize_t write_packet_data_size(struct kobject *kobj, char *buffer,
> + loff_t pos, size_t count)
80-cols.
> +static ssize_t read_rbu_data_size(struct kobject *kobj, char *buffer,
> + loff_t pos, size_t count)
ditto.
> +{
> + unsigned int size = 0;
> + if (pos == 0) {
> + lock_rbu_access(&rbu_data.lock);
> + size = sprintf(buffer, "%lu\n", rbu_data.bios_image_size);
> + unlock_rbu_access(&rbu_data.lock);
Unneeded or incorrect locking?
> +*/
> +static ssize_t write_rbu_data_size(struct kobject *kobj, char *buffer,
> + loff_t pos, size_t count)
80-cols?
> +static ssize_t read_rbu_data(struct kobject *kobj, char *buffer,
> + loff_t pos, size_t count)
> +{
> + unsigned char *ptemp = NULL;
> + int retVal =0;
> + int bytes_left = 0;
> + int data_length = 0;
Unneeded initialisations. Please review all the code for that.
s/retVal/retval/
> + data_length = ((bytes_left > count) ? count : bytes_left);
max()?
> + ptemp = (char *)rbu_data.image_update_buffer;
unneeded typecast
> + memcpy(buffer, (char *)(ptemp + pos), data_length);
unneeded typecast
> + if ( pos > imagesize ) {
Quite a mix of coding styles in this patch!
> + data_length = ((bytes_left > count) ? count : bytes_left);
max()
> + if ((retVal = packet_read_list(ptempBuf, &data_length)) < 0)
> + goto read_rbu_data_exit;
> +
> + if ((pos + count) > imagesize) {
> + rbu_data.packet_read_count = 0;
> + /* this was the last copy */
> + retVal = bytes_left;
> + }
> + else
> + retVal = count;
> +
} else {
retVal = count;
}
(opinions differ...)
> + sysfs_create_bin_file(&rbu_subsys.kset.kobj, &rbudata_attr );
> + sysfs_create_bin_file(&rbu_subsys.kset.kobj, &rbudatasize_attr );
> + sysfs_create_bin_file(&rbu_subsys.kset.kobj, &packetdatasize_attr );
> + sysfs_create_bin_file(&rbu_subsys.kset.kobj, &packetdata_attr );
> + sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudata_attr );
> + sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &rbudatasize_attr );
> + sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdatasize_attr );
> + sysfs_remove_bin_file(&rbu_subsys.kset.kobj, &packetdata_attr );
whitepsace
>
> +config DELL_RBU
> + bool "BIOS update support for DELL systems via sysfs"
> + default n
> + help
> + Say Y if you want to have the option of updating the BIOS for your
> + DELL system. Note you need a supporting application to comunicate
> + with the BIOS regardign the new image for the image update to
> + take effect.
> +
> + See <file:Documentation/DELL_RBU.txt> for more details on the driver.
> +
Should this feature be available for all architectures, or only for X86 ||
X86_64? (If it compiles OK for other architectures then I'd leave it
as-is, for coverage).
The patch seems to alternate between using hard tabs and using
eight-spaces. Hard tabs are preferred. Please hunt that down and fix it
up.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]