2014/09/24

Linux iSCSI Target Implementation

iSCSI command execution
-------------------
target_submit_cmd
target_submit_cmd_map_sgls
target_setup_cmd_from_cdb
  transport->parse_cdb(sbc_parse_cdb)
    sbc_execute_rw
      cmd->execute_rw // se_cmd.execute_rw points to sbc_ops->execute_rw

// destination to file      
fd_sbc_ops.execute_rw(fd_execute_rw)
  fd_do_rw
    vfs_readv
    vfs_writev
     
// destination to block device  
iblock_sbc_ops.execute_rw(iblock_execute_rw)
  // block device read write
  iblock_submit_bios
    block_lba = (cmd->t_task_lba << 3);
    submit_bio

     
iSCSI communication      
-------------------
iscsit_do_tx_data
  kernel_sendmsg
iscsit_do_rx_data
  kernel_recvmsg

iscsit_accept_np
  kernel_accept
iscsit_setup_np
  kernel_bind



iSCSI Authentication      
-------------------
__iscsi_target_login_thread
iscsi_target_start_negotiation
iscsi_target_do_login
iscsi_target_handle_csg_zero
iscsi_target_do_authentication
iscsi_handle_authentication

Flash in Linux

NAND & Driver

struct nand_chip {
 int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
   uint32_t offset, int data_len, const uint8_t *buf,
   int oob_required, int page, int cached, int raw);
       
           
}
nand_write
  nand_do_write_ops
    chip->write_page ( nand_write_page )
      ecc->write_page ( nand_write_page_hwecc )
        chip->write_buf ( s3c2410_nand_write_buf )
      chip->cmdfunc( nand_command )
        chip->cmd_ctrl( s3c2410_nand_hwcontrol )

nand_read
  nand_do_read_ops
    chip->cmdfunc ( nand_command )
      chip->cmd_ctrl( s3c2410_nand_hwcontrol )
    ecc->read_page_raw ( nand_read_page_raw )
      chip->read_buf ( s3c2410_nand_read_buf )


MTD Layer

mtd_read
  mtd->_read ( nand_read )
mtd_write    
  mtd->_write ( nand_write )


UBI Layer

ubi_io_write
  mtd_write
ubi_io_read
  mtd_read

ubi_attach
  scan_fast
    scan_peb
  scan_all
    scan_peb
      ubi_io_read_ec_hdr ( check if the header is ok or not )
      ubi_add_to_av
        add_volume
        p = &av->root.rb_node;
     
        rb_link_node(&aeb->u.rb, parent, p);
        rb_insert_color(&aeb->u.rb, &av->root);


ubi_attach_mtd_dev
  io_init
    ubi->peb_size   = ubi->mtd->erasesize;

ubi_vid_hdr's lnum holds the logical block number
ubi_ainf_peb, attach information about a physical eraseblock, lnum

ubi_eba_init
  vol->eba_tbl = kmalloc
  av = ubi_find_av(ai, idx2vol_id(ubi, i));
  vol->eba_tbl[aeb->lnum] = aeb->pnum;
Wear-Leveling is in the drivers\mtd\ubi\wl.c


UBI FS

UBIFS to UBI
  ubifs_leb_read
    ubi_read
      ubi_leb_read
  ubifs_leb_write
    ubi_leb_write


UBIFS's VFS mount point
  fs\ubifs\file.c
  fs\ubifs\dir.c
  fs\ubifs\super.c

2014/09/23

PCI MSI Setup

The Root Complex(RC) controller receives the MSI messages send from PCI device, then RC raise an interrupt to CPU.

During the startup, software tells PCI device the address to where MSI message should be written. The address is mapped to RC controller IO space.

An example implementation:
http://lxr.free-electrons.com/source/arch/arm/mach-iop13xx/msi.c

The call stack:
__write_msi_msg
write_msi_msg
arch_setup_msi_irq

in arch_setup_msi_irq(), the address (to where MSI message is written) is defined as below.
  msg.address_hi = 0x0;
  msg.address_lo = IOP13XX_MU_MIMR_PCI;

in __write_msi_msg(), the address is actually written to device configuration register (message address register).
http://lxr.free-electrons.com/source/drivers/pci/msi.c#L327

2014/09/16

Linux Timer Interface

/* Initialize a simple mmio based clocksource */
clocksource_mmio_init

/* set up the handler of timer interrupt ,
In Irq handler, clock_event_device.event_handler will be called.
event_handler: Assigned by the framework to be called by the low level handler of the event source
*/
setup_irq

/* Configure and register a clock event device */
clockevents_config_and_register

2014/09/11

PCI BAR Assignment

Question:
How PCI BAR is assigned in Linux?

Answer:
Find free space from PCI bus resource in __find_resource, in which resource start and end are allocated ( from the bus IO memory region? )
__pci_assign_resource
pci_bus_alloc_resource
pci_bus_alloc_from_region
allocate_resource
reallocate_resource
__find_resource

In below function, parameter resno is the BAR number. 
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, int resno, resource_size_t size, resource_size_t align)


PCI BAR address points device IO. The value of the BAR address is just mapped by OS, not in range of the host physical memory. 

To update the BAR to PCI device. 
pci_assign_resource  // from the res->start ... 
  pci_update_resource
    pcibios_resource_to_bus(dev->bus, &region, res);
      region->start = res->start - offset;
    new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
    reg = pci_resource_bar(dev, resno, &type);   
    pci_write_config_dword(dev, reg, new);




Reference:
http://www.linux-mips.org/wiki/PCI_Subsystem
http://wiki.osdev.org/PCI

2014/09/09

Linux Interrupt on ARM

Question:
How interrupt information get initialized?

Answers:
Usually, in place like arch/arm/mach-xxx/some_file.c, resource of tyep IORESOURCE_IRQ is defined and get registered by calling platform_device_register(struct platform_device).
platform_device.name is used to map device and  device driver.

Device driver registration by usually happens when device module is loaded, by calling platform_driver_register(struct platform_driver).
platform_driver.driver.name is used map the device and device driver.



Question:
How driver register interrupt handler?

Answer:
In implementation of function platform_driver.probe(),
irq = platform_get_irq(plat_dev, num) // get an IRQ for a device
request_irq(irq, my_driver_irq_handler, IRQF_TRIGGER_RISING, ... )

Then my_driver_irq_handler() will be called when irq with index "num" is raised.



Question:
ARM CPU has only one IRQ input pin, how could it find out which peripheral raised the interrupt?
How driver interrupt handler get called?

Answer:
The Interrupt could be handled in two ways, call stack below:

1. CONFIG_MULTI_IRQ_HANDLER on
/* Macro in entry-armV.S */
irq_handler
/* This is a function pointer set by set_handle_irq, usually it got set during platform initialization, ex, like in function s3c2410_init_irq(), the pointer points to s3c24xx_handle_irq().
The pointer could also be set in setup_arch(), /arch/arm/kernel/setup.c, as mdesc->handle_irq.
mdesc->handle_irq is initialized in macro MACHINE_START, MACHINE_END,
mdesc->handle_irq could be set as gic_handle_irq(). */
handle_arch_irq
/* Here finds the actually irq number!!! */
s3c24xx_handle_irq
handle_IRQ
/* the kernel irq API, which will invoke actually irq handler. */
generic_handle_irq

2. CONFIG_MULTI_IRQ_HANDLER off
/* Macro in entry-armV.S */
irq_handler
/* get_irqnr_and_base() is called, which is a platform specific macro to query irq controller to find out which irq line is raised. */
arch_irq_handler_default
asm_do_IRQ
handle_IRQ
/* the kernel irq API, which will invoke actually irq handler. */
generic_handle_irq



Question:
When ARM's GIC ( General Interrupt Controller) hardware is used, how IRQ number is retrieved?

Answer:
In file arch/arm/common/gic.c, function gic_handle_irq() reads the interrupt controller's CPU interface register ICCIAR ( interrupt acknowledge register ) to get the IRQ number.  The register has 0x08 offset relative to "CPU interface base address".
Code:
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
Reference:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0048b/index.html


Reference:
http://lxr.free-electrons.com/source/Documentation/blockdev/mflash.txt
http://pankaj-techstuff.blogspot.jp/2007/11/story-interrupt-handling-in-linux-2611.html
http://hi.baidu.com/sun_yfs/blog/item/e2531ecbf2cde989c9176806.html
http://lxr.linux.no/#linux+v2.6.32.59/drivers/video/s3c2410fb.c
http://lxr.linux.no/#linux+v2.6.32.59/arch/arm/plat-s3c24xx/irq.c
http://code.metager.de/source/xref/denx/u-boot/doc/README.SPL
http://code.metager.de/source/xref/denx/u-boot/doc/README.arm-relocation

Post Code on Blogger

Simplest way to post code to blogger for me: <pre style="background: #f0f0f0; border: 1px dashed #CCCCCC; color: black;overflow-x:...