From 0b7af4067f455fc1e553585f6aa50437bd26c8af Mon Sep 17 00:00:00 2001 From: vm03 Date: Sat, 15 Nov 2014 19:17:58 +0300 Subject: [PATCH 001/104] Fix sizeof-pointer-memaccess warning with gcc 4.8 --- net/bluetooth/hci_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 9ffd0f1ab18f..d7356b653498 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -404,7 +404,7 @@ void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]) memset(&cp, 0, sizeof(cp)); cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(ltk)); + memcpy(cp.ltk, ltk, sizeof(cp.ltk)); hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); } From 581ddc97e54753077083986c1e2c0127b1c53779 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sat, 15 Nov 2014 19:19:09 +0300 Subject: [PATCH 002/104] disable SENSORS_BMA2X2 --- arch/arm/configs/w5ds_global_com_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index 0b350cebf975..216c9287c56d 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -285,7 +285,7 @@ CONFIG_INPUT_EVDEV=y CONFIG_INPUT_EVBUG=m CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_SENSOR=y -CONFIG_SENSORS_BMA2X2=y +# CONFIG_SENSORS_BMA2X2 is not set CONFIG_SENSORS_BMM050=y CONFIG_SENSOR_APDS9130=y CONFIG_INPUT_TOUCHSCREEN=y From 6d9a32313f11caa6d35cf4521203577cbe94ca65 Mon Sep 17 00:00:00 2001 From: Deepak Verma Date: Wed, 29 Jan 2014 16:44:43 +0530 Subject: [PATCH 003/104] msm: vidc: Add support for different errors When hardware is overloaded or when max number of clients are reached in driver or firmware, hardware error is sent to video client. This change is to replace hardware error with actual errors. CRs-fixed: 575852 Change-Id: I07e599f894a3716a3dc4fed5eb7c987311f5bdde Signed-off-by: Deepak Verma --- .../platform/msm/vidc/hfi_response_handler.c | 61 +++++++++++++------ .../media/platform/msm/vidc/msm_vidc_common.c | 47 ++++++++++---- .../media/platform/msm/vidc/vidc_hfi_api.h | 2 +- include/linux/msm_vidc_dec.h | 2 + include/linux/msm_vidc_enc.h | 3 +- include/linux/videodev2.h | 2 + 6 files changed, 85 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c index f4ad985eb17f..c6fb382b3581 100644 --- a/drivers/media/platform/msm/vidc/hfi_response_handler.c +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -49,7 +49,7 @@ static enum vidc_status hfi_map_err_status(int hfi_err) vidc_err = VIDC_ERR_NOT_SUPPORTED; break; case HFI_ERR_SYS_MAX_SESSIONS_REACHED: - vidc_err = VIDC_ERR_MAX_CLIENT; + vidc_err = VIDC_ERR_MAX_CLIENTS; break; case HFI_ERR_SYS_SESSION_IN_USE: vidc_err = VIDC_ERR_CLIENT_PRESENT; @@ -75,6 +75,8 @@ static enum vidc_status hfi_map_err_status(int hfi_err) vidc_err = VIDC_ERR_FAIL; break; } + if (vidc_err != HFI_ERR_NONE) + dprintk(VIDC_ERR, "HFI Error: %d\n", vidc_err); return vidc_err; } @@ -108,7 +110,8 @@ static void hfi_process_sess_evt_seq_changed( struct hfi_frame_size frame_sz; u8 *data_ptr; int prop_id; - dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY"); + dprintk(VIDC_DBG, "RECEIVED: EVENT_NOTIFY[%u]: %d, 0x%x\n", + pkt->session_id, pkt->event_data1, pkt->event_data2); if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) { dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size"); @@ -248,27 +251,33 @@ static void hfi_process_event_notify( hfi_process_sys_error(callback, device_id); break; case HFI_EVENT_SESSION_ERROR: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR"); + dprintk(VIDC_INFO, + "HFI_EVENT_SESSION_ERROR[%u]\n", pkt->session_id); if (!validate_session_pkt(sessions, sess, session_lock)) hfi_process_session_error(callback, device_id, pkt); break; case HFI_EVENT_SESSION_SEQUENCE_CHANGED: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED"); + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%u]\n", + pkt->session_id); if (!validate_session_pkt(sessions, sess, session_lock)) hfi_process_sess_evt_seq_changed(callback, device_id, pkt); break; case HFI_EVENT_SESSION_PROPERTY_CHANGED: - dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED"); + dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED[%u]\n", + pkt->session_id); break; case HFI_EVENT_RELEASE_BUFFER_REFERENCE: - dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE\n"); + dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%u]\n", + pkt->session_id); if (!validate_session_pkt(sessions, sess, session_lock)) hfi_process_evt_release_buffer_ref(callback, device_id, pkt); break; default: - dprintk(VIDC_WARN, "hal_process_event_notify:unkown_event_id"); + dprintk(VIDC_WARN, + "hal_process_event_notify: unknown_event_id[%u]\n", + pkt->session_id); break; } } @@ -758,7 +767,8 @@ static void hfi_process_session_prop_info( struct msm_vidc_cb_cmd_done cmd_done; struct buffer_requirements buff_req; - dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO"); + dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%u]\n", + pkt->session_id); if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) { dprintk(VIDC_ERR, "hal_process_session_prop_info:bad_pkt_size"); @@ -800,7 +810,8 @@ static void hfi_process_session_init_done( struct msm_vidc_cb_cmd_done cmd_done; struct vidc_hal_session_init_done session_init_done; struct hal_session *sess_close = NULL; - dprintk(VIDC_DBG, "RECEIVED:SESSION_INIT_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%u]\n", + pkt->session_id); if (sizeof(struct hfi_msg_sys_session_init_done_packet) > pkt->size) { dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size"); @@ -839,7 +850,8 @@ static void hfi_process_session_load_res_done( struct hfi_msg_session_load_resources_done_packet *pkt) { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_LOAD_RESOURCES_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%u]\n", + pkt->session_id); if (sizeof(struct hfi_msg_session_load_resources_done_packet) != pkt->size) { @@ -865,7 +877,8 @@ static void hfi_process_session_flush_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_FLUSH_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%u]\n", + pkt->session_id); if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) { dprintk(VIDC_ERR, "hal_process_session_flush_done: " @@ -889,7 +902,8 @@ static void hfi_process_session_etb_done( { struct msm_vidc_cb_data_done data_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_ETB_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size < sizeof(struct hfi_msg_session_empty_buffer_done_packet)) { @@ -930,7 +944,8 @@ static void hfi_process_session_ftb_done( session = (struct hal_session *) ((struct hal_session *) pack->session_id)->session_id; - dprintk(VIDC_DBG, "RECEIVED:SESSION_FTB_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%u]\n", + pack->session_id); memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done)); @@ -1028,7 +1043,8 @@ static void hfi_process_session_start_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_START_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_start_done_packet)) { @@ -1053,7 +1069,8 @@ static void hfi_process_session_stop_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_STOP_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_stop_done_packet)) { @@ -1078,7 +1095,8 @@ static void hfi_process_session_rel_res_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_RESOURCES_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_release_resources_done_packet)) { @@ -1102,6 +1120,8 @@ static void hfi_process_session_rel_buf_done( struct hfi_msg_session_release_buffers_done_packet *pkt) { struct msm_vidc_cb_cmd_done cmd_done; + dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_session_release_buffers_done_packet)) { @@ -1129,7 +1149,8 @@ static void hfi_process_session_end_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE"); + dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_sys_session_end_done_packet)) { @@ -1154,8 +1175,8 @@ static void hfi_process_session_abort_done( { struct msm_vidc_cb_cmd_done cmd_done; - dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE"); - + dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%u]\n", + pkt->session_id); if (!pkt || pkt->size != sizeof(struct hfi_msg_sys_session_abort_done_packet)) { dprintk(VIDC_ERR, "%s: bad packet/packet size: %d", @@ -1184,6 +1205,8 @@ static void hfi_process_session_get_seq_hdr_done( dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size); return; } + dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%u]\n", + pkt->session_id); memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done)); data_done.device_id = device_id; data_done.size = sizeof(struct msm_vidc_cb_data_done); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 82ca2403d8ff..e2ece0172aab 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -390,8 +390,9 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst, &inst->completions[SESSION_MSG_INDEX(cmd)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", - SESSION_MSG_INDEX(cmd)); + dprintk(VIDC_ERR, + "%s: Wait interrupted or timeout[%u]: %d\n", + __func__, (u32)inst->session, SESSION_MSG_INDEX(cmd)); msm_comm_recover_from_session_error(inst); rc = -EIO; } else { @@ -439,6 +440,21 @@ static void msm_comm_generate_session_error(struct msm_vidc_inst *inst) mutex_unlock(&inst->lock); } +static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst) +{ + if (!inst) { + dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__); + return; + } + mutex_lock(&inst->sync_lock); + inst->session = NULL; + inst->state = MSM_VIDC_CORE_INVALID; + msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_MAX_CLIENTS); + dprintk(VIDC_WARN, + "%s: Too many clients\n", __func__); + mutex_unlock(&inst->sync_lock); +} + static void handle_session_init_done(enum command_response cmd, void *data) { struct msm_vidc_cb_cmd_done *response = data; @@ -476,7 +492,10 @@ static void handle_session_init_done(enum command_response cmd, void *data) dprintk(VIDC_ERR, "Session init response from FW : 0x%x", response->status); - msm_comm_generate_session_error(inst); + if (response->status == VIDC_ERR_MAX_CLIENTS) + msm_comm_generate_max_clients_error(inst); + else + msm_comm_generate_session_error(inst); } signal_session_msg_receipt(cmd, inst); } else { @@ -1479,8 +1498,8 @@ static int msm_comm_unset_ocmem(struct msm_vidc_core *core) &core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", - SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)); + dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n", + __func__, SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)); rc = -EIO; } release_ocmem_failed: @@ -1502,8 +1521,8 @@ static int msm_comm_init_core_done(struct msm_vidc_inst *inst) &core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", - SYS_MSG_INDEX(SYS_INIT_DONE)); + dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n", + __func__, SYS_MSG_INDEX(SYS_INIT_DONE)); rc = -EIO; goto exit; } else { @@ -2583,7 +2602,8 @@ int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst) msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { dprintk(VIDC_ERR, - "Wait interrupted or timeout: %d\n", + "%s: Wait interrupted or timeout[%u]: %d\n", + __func__, (u32)inst->session, SESSION_MSG_INDEX(SESSION_PROPERTY_INFO)); inst->state = MSM_VIDC_CORE_INVALID; msm_comm_recover_from_session_error(inst); @@ -3360,7 +3380,11 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) mutex_lock(&inst->sync_lock); inst->state = MSM_VIDC_CORE_INVALID; mutex_unlock(&inst->sync_lock); - msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR); + msm_vidc_queue_v4l2_event(inst, + V4L2_EVENT_MSM_VIDC_HW_OVERLOAD); + dprintk(VIDC_WARN, + "%s: Hardware is overloaded\n", __func__); + wake_up(&inst->kernel_event_queue); } return rc; } @@ -3410,8 +3434,9 @@ int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst) &inst->completions[SESSION_MSG_INDEX(SESSION_ABORT_DONE)], msecs_to_jiffies(msm_vidc_hw_rsp_timeout)); if (!rc) { - dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n", - __func__, SESSION_MSG_INDEX(SESSION_ABORT_DONE)); + dprintk(VIDC_ERR, "%s: Wait interrupted or timeout[%u]: %d\n", + __func__, (u32)inst->session, + SESSION_MSG_INDEX(SESSION_ABORT_DONE)); msm_comm_generate_sys_error(inst); } else change_inst_state(inst, MSM_VIDC_CLOSE_DONE); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index c73751443ec9..849cf8116d5f 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -63,7 +63,7 @@ enum vidc_status { VIDC_ERR_BAD_HANDLE, VIDC_ERR_NOT_SUPPORTED, VIDC_ERR_BAD_STATE, - VIDC_ERR_MAX_CLIENT, + VIDC_ERR_MAX_CLIENTS, VIDC_ERR_IFRAME_EXPECTED, VIDC_ERR_HW_FATAL, VIDC_ERR_BITSTREAM_ERR, diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h index 9301006eb40a..1c3337cb2f8e 100644 --- a/include/linux/msm_vidc_dec.h +++ b/include/linux/msm_vidc_dec.h @@ -58,6 +58,8 @@ #define VDEC_MSG_EVT_HW_ERROR (VDEC_MSG_BASE + 14) #define VDEC_MSG_EVT_INFO_CONFIG_CHANGED (VDEC_MSG_BASE + 15) #define VDEC_MSG_EVT_INFO_FIELD_DROPPED (VDEC_MSG_BASE + 16) +#define VDEC_MSG_EVT_HW_OVERLOAD (VDEC_MSG_BASE + 17) +#define VDEC_MSG_EVT_MAX_CLIENTS (VDEC_MSG_BASE + 18) /*Buffer flags bits masks.*/ #define VDEC_BUFFERFLAG_EOS 0x00000001 diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h index 4ce3db188399..36625a70cb1c 100644 --- a/include/linux/msm_vidc_enc.h +++ b/include/linux/msm_vidc_enc.h @@ -45,7 +45,8 @@ #define VEN_MSG_RESUME 9 #define VEN_MSG_STOP_READING_MSG 10 #define VEN_MSG_LTRUSE_FAILED 11 - +#define VEN_MSG_HW_OVERLOAD 12 +#define VEN_MSG_MAX_CLIENTS 13 /*Buffer flags bits masks*/ #define VEN_BUFFLAG_EOS 0x00000001 diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f446f5168141..2455bbcc0110 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2500,6 +2500,8 @@ struct v4l2_streamparm { (V4L2_EVENT_MSM_VIDC_START + 6) #define V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER \ (V4L2_EVENT_MSM_VIDC_START + 7) +#define V4L2_EVENT_MSM_VIDC_HW_OVERLOAD (V4L2_EVENT_MSM_VIDC_START + 8) +#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9) /* Payload for V4L2_EVENT_VSYNC */ struct v4l2_event_vsync { From 0648d6448bd8e28c0682fc3e27514b4502b41360 Mon Sep 17 00:00:00 2001 From: Arun Menon Date: Wed, 12 Mar 2014 15:18:45 -0700 Subject: [PATCH 004/104] msm: vidc: configure and set hier-p layers Firmware requires the max number of hier-p layers to be used during the encode session to be set in load resources state. Without this change, firmware will not enable hier-p encoding. Also switch to using HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER to set the number of hier-p layers. Change-Id: I1fbf835acdb7d0a06d33cf9c2d038fb87c10010d Signed-off-by: Arun Menon --- .../platform/msm/vidc/hfi_packetization.c | 12 ++++- drivers/media/platform/msm/vidc/msm_venc.c | 44 ++++++++++++++++++- .../media/platform/msm/vidc/vidc_hfi_api.h | 3 +- .../media/platform/msm/vidc/vidc_hfi_helper.h | 6 ++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index 4b7a3bead7a9..cdc649f6eb83 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -1479,10 +1479,18 @@ int create_pkt_cmd_session_set_property( pr_err("MARK LTR\n"); break; } - case HAL_PARAM_VENC_HIER_P_NUM_FRAMES: + case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS: { pkt->rg_property_data[0] = - HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER; + HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER; + pkt->rg_property_data[1] = *(u32 *)pdata; + pkt->size += sizeof(u32) * 2; + break; + } + case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES: + { + pkt->rg_property_data[0] = + HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER; pkt->rg_property_data[1] = *(u32 *)pdata; pkt->size += sizeof(u32) * 2; break; diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index d67929569043..aee9d826c373 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -992,11 +992,53 @@ static int msm_venc_queue_setup(struct vb2_queue *q, return rc; } +static int msm_venc_enable_hier_p(struct msm_vidc_inst *inst) +{ + int num_enh_layers = 0; + u32 property_id = 0; + struct hfi_device *hdev = NULL; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + if (inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_VP8) + return 0; + + num_enh_layers = inst->capability.hier_p.max - 1; + if (!num_enh_layers) + return 0; + + hdev = inst->core->device; + property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS; + + rc = call_hfi_op(hdev, session_set_property, + (void *)inst->session, property_id, + (void *)&num_enh_layers); + if (rc) { + dprintk(VIDC_ERR, + "%s: failed with error = %d\n", __func__, rc); + } + return rc; +} + static inline int start_streaming(struct msm_vidc_inst *inst) { int rc = 0; struct vb2_buf_entry *temp; struct list_head *ptr, *next; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + + rc = msm_venc_enable_hier_p(inst); + if (rc) + return rc; + if (inst->capability.pixelprocess_capabilities & HAL_VIDEO_ENCODER_SCALING_CAPABILITY) rc = msm_comm_check_scaling_supported(inst); @@ -2093,7 +2135,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &markltr; break; case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS: - property_id = HAL_PARAM_VENC_HIER_P_NUM_FRAMES; + property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES; hier_p_layers = ctrl->val; if (hier_p_layers > (inst->capability.hier_p.max - 1)) { dprintk(VIDC_ERR, diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 849cf8116d5f..56c1a7b5292f 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -186,7 +186,8 @@ enum hal_property { HAL_CONFIG_VENC_MARKLTRFRAME, HAL_CONFIG_VENC_USELTRFRAME, HAL_CONFIG_VENC_LTRPERIOD, - HAL_PARAM_VENC_HIER_P_NUM_FRAMES, + HAL_CONFIG_VENC_HIER_P_NUM_FRAMES, + HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS, }; enum hal_domain { diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 5117266d44e9..7f4dd04a3615 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -305,8 +305,6 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018) #define HFI_PROPERTY_PARAM_VENC_MULTIREF_P \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019) -#define HFI_PROPERTY_PARAM_VENC_HIER_P_NUM_ENH_LAYER \ - (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01A) #define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B) #define HFI_PROPERTY_PARAM_VENC_LTRMODE \ @@ -319,6 +317,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F) #define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020) +#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) #define HFI_PROPERTY_CONFIG_VENC_COMMON_START \ (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000) #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \ @@ -342,6 +342,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009) #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME \ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A) +#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER \ + (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B) #define HFI_PROPERTY_CONFIG_VENC_LTRPERIOD \ (HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C) #define HFI_PROPERTY_CONFIG_VPE_COMMON_START \ From 140426351c94c18474e784aca38f89da5ed5f32f Mon Sep 17 00:00:00 2001 From: Ashray Kulkarni Date: Wed, 12 Feb 2014 20:08:06 -0800 Subject: [PATCH 005/104] msm: vidc: Add support for setting initial qp Adds support to set initial qp, thereby allowing the client to set initial qp for I,P, and B frames. Change-Id: Ie956651bde85e800d97a0007769af9aec8ca16a4 Signed-off-by: Ashray Kulkarni Conflicts: drivers/media/platform/msm/vidc/msm_venc.c --- .../platform/msm/vidc/hfi_packetization.c | 14 +++++ drivers/media/platform/msm/vidc/msm_venc.c | 60 +++++++++++++++++++ .../media/platform/msm/vidc/vidc_hfi_api.h | 8 +++ .../media/platform/msm/vidc/vidc_hfi_helper.h | 9 +++ include/linux/videodev2.h | 12 ++++ 5 files changed, 103 insertions(+) diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index cdc649f6eb83..1ca85bddb303 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -1495,6 +1495,20 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32) * 2; break; } + case HAL_PARAM_VENC_ENABLE_INITIAL_QP: + { + struct hfi_initial_quantization *hfi; + struct hal_initial_quantization *quant = pdata; + pkt->rg_property_data[0] = + HFI_PROPERTY_PARAM_VENC_INITIAL_QP; + hfi = (struct hfi_initial_quantization *) &pkt->rg_property_data[1]; + hfi->init_qp_enable = quant->initqp_enable; + hfi->qp_i = quant->qpi; + hfi->qp_p = quant->qpp; + hfi->qp_b = quant->qpb; + pkt->size += sizeof(u32) + sizeof(struct hfi_initial_quantization); + break; + } /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HAL_CONFIG_BUFFER_REQUIREMENTS: case HAL_CONFIG_PRIORITY: diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index aee9d826c373..9fa439de6964 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -811,6 +811,45 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .step = 1, .qmenu = NULL, .cluster = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP, + .name = "Enable setting initial QP", + .type = V4L2_CTRL_TYPE_BUTTON, + .minimum = 0, + .maximum = 0, + .default_value = 0, + .step = 0, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP, + .name = "Iframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP, + .name = "Pframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, + }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP, + .name = "Bframe initial QP", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 1, + .maximum = 51, + .default_value = 1, + .step = 1, + .qmenu = NULL, } }; @@ -2185,6 +2224,7 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, u32 property_id = 0; void *pdata = NULL; struct msm_vidc_core_capability *cap = NULL; + struct hal_initial_quantization quant; if (!inst || !inst->core || !inst->core->device || !ctrl) { dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); @@ -2229,6 +2269,26 @@ static int try_set_ext_ctrl(struct msm_vidc_inst *inst, property_id = HAL_PARAM_VENC_LTRMODE; pdata = <rmode; break; + case V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP: + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + quant.initqp_enable = control[i].value; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP: + quant.qpi = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP: + quant.qpp = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; + case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP: + quant.qpb = control[i].value; + property_id = HAL_PARAM_VENC_ENABLE_INITIAL_QP; + pdata = &quant; + break; default: dprintk(VIDC_ERR, "Invalid id set: %d\n", control[i].id); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 56c1a7b5292f..d0748be5ffc8 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -188,6 +188,7 @@ enum hal_property { HAL_CONFIG_VENC_LTRPERIOD, HAL_CONFIG_VENC_HIER_P_NUM_FRAMES, HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS, + HAL_PARAM_VENC_ENABLE_INITIAL_QP, }; enum hal_domain { @@ -633,6 +634,13 @@ struct hal_quantization { u32 layer_id; }; +struct hal_initial_quantization { + u32 qpi; + u32 qpp; + u32 qpb; + u32 initqp_enable; +}; + struct hal_quantization_range { u32 min_qp; u32 max_qp; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 7f4dd04a3615..7c62e77ca59d 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -319,6 +319,8 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020) #define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026) +#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP \ + (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x028) #define HFI_PROPERTY_CONFIG_VENC_COMMON_START \ (HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000) #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE \ @@ -533,6 +535,13 @@ struct hfi_quantization { u32 layer_id; }; +struct hfi_initial_quantization { + u32 qp_i; + u32 qp_p; + u32 qp_b; + u32 init_qp_enable; +}; + struct hfi_quantization_range { u32 min_qp; u32 max_qp; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 2455bbcc0110..174829da8c69 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1935,6 +1935,18 @@ enum v4l2_mpeg_vidc_video_ltrmode { #define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 43) +#define V4L2_CID_MPEG_VIDC_VIDEO_ENABLE_INITIAL_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 44) + +#define V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 45) + +#define V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 46) + +#define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 47) + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) #define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1) From 6a7d166157c8ee66da5553dec7b062eae815563b Mon Sep 17 00:00:00 2001 From: Jayasena Sangaraboina Date: Thu, 26 Sep 2013 17:58:03 -0700 Subject: [PATCH 006/104] msm: vidc: Add AFD and CC metadata support to extradata Add support and control for setting Active format description and closed caption meta data in the extradata. FW parses metadata and adds it to the extradata. Client can use control to parse extradata for the metadata information. Change-Id: I79fb71e635927c95e0792862c9dea7d96f58e895 Signed-off-by: Jayasena Sangaraboina --- .../platform/msm/vidc/hfi_packetization.c | 9 ++-- drivers/media/platform/msm/vidc/msm_vdec.c | 7 ++- drivers/media/platform/msm/vidc/msm_venc.c | 2 - .../media/platform/msm/vidc/msm_vidc_common.c | 9 ++-- drivers/media/platform/msm/vidc/vidc_hfi.h | 9 ++-- .../media/platform/msm/vidc/vidc_hfi_api.h | 3 +- include/linux/videodev2.h | 47 ++++++++++--------- include/media/msm_vidc.h | 11 +++++ 8 files changed, 48 insertions(+), 49 deletions(-) diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index 1ca85bddb303..a563f68917d6 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -342,12 +342,6 @@ static int get_hfi_extradata_index(enum hal_extradata_id index) case HAL_EXTRADATA_RECOVERY_POINT_SEI: ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA; break; - case HAL_EXTRADATA_CLOSED_CAPTION_UD: - ret = HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA; - break; - case HAL_EXTRADATA_AFD_UD: - ret = HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA; - break; case HAL_EXTRADATA_MULTISLICE_INFO: ret = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO; break; @@ -372,6 +366,9 @@ static int get_hfi_extradata_index(enum hal_extradata_id index) case HAL_EXTRADATA_METADATA_MBI: ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING; break; + case HAL_EXTRADATA_STREAM_USERDATA: + ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA; + break; default: dprintk(VIDC_WARN, "Extradata index not found: %d\n", index); break; diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 66d6878e7887..30c71a342017 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -220,7 +220,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .name = "Extradata Type", .type = V4L2_CTRL_TYPE_MENU, .minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE, - .maximum = V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO, + .maximum = V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA, .default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE, .menu_skip_mask = ~( (1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) | @@ -233,8 +233,6 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_AFD_UD) | (1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) | (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) | @@ -243,7 +241,8 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { (1 << V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO) | (1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) | (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) + (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) | + (1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA) ), .qmenu = mpeg_video_vidc_extradata, .step = 0, diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 9fa439de6964..d7f33d7bc4b3 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -689,8 +689,6 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { (1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) | (1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) | (1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD) | - (1 << V4L2_MPEG_VIDC_EXTRADATA_AFD_UD) | (1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) | (1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) | (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) | diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index e2ece0172aab..2f6d7535b966 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3150,12 +3150,6 @@ enum hal_extradata_id msm_comm_get_hal_extradata_index( case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI: ret = HAL_EXTRADATA_RECOVERY_POINT_SEI; break; - case V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD: - ret = HAL_EXTRADATA_CLOSED_CAPTION_UD; - break; - case V4L2_MPEG_VIDC_EXTRADATA_AFD_UD: - ret = HAL_EXTRADATA_AFD_UD; - break; case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO: ret = HAL_EXTRADATA_MULTISLICE_INFO; break; @@ -3183,6 +3177,9 @@ enum hal_extradata_id msm_comm_get_hal_extradata_index( case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI: ret = HAL_EXTRADATA_METADATA_MBI; break; + case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA: + ret = HAL_EXTRADATA_STREAM_USERDATA; + break; default: dprintk(VIDC_WARN, "Extradata not found: %d\n", index); break; diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h index 75f583ff0dc7..55663381d156 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi.h @@ -79,9 +79,8 @@ #define HFI_EXTRADATA_FRAME_RATE 0x00000007 #define HFI_EXTRADATA_PANSCAN_WINDOW 0x00000008 #define HFI_EXTRADATA_RECOVERY_POINT_SEI 0x00000009 -#define HFI_EXTRADATA_CLOSED_CAPTION_UD 0x0000000A -#define HFI_EXTRADATA_AFD_UD 0x0000000B #define HFI_EXTRADATA_MPEG2_SEQDISP 0x0000000D +#define HFI_EXTRADATA_STREAM_USERDATA 0x0000000E #define HFI_EXTRADATA_FRAME_QP 0x0000000F #define HFI_EXTRADATA_FRAME_BITS_INFO 0x00000010 #define HFI_EXTRADATA_MULTISLICE_INFO 0x7F100000 @@ -190,10 +189,6 @@ struct hfi_extradata_header { #define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY \ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E) -#define HFI_PROPERTY_PARAM_VDEC_CLOSED_CAPTION_EXTRADATA \ - (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00F) -#define HFI_PROPERTY_PARAM_VDEC_AFD_EXTRADATA \ - (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x010) #define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA \ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011) #define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA \ @@ -206,6 +201,8 @@ struct hfi_extradata_header { (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015) #define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016) +#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA \ + (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x017) #define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA \ (HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018) #define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \ diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index d0748be5ffc8..35beeec097ae 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -91,8 +91,6 @@ enum hal_extradata_id { HAL_EXTRADATA_FRAME_RATE, HAL_EXTRADATA_PANSCAN_WINDOW, HAL_EXTRADATA_RECOVERY_POINT_SEI, - HAL_EXTRADATA_CLOSED_CAPTION_UD, - HAL_EXTRADATA_AFD_UD, HAL_EXTRADATA_MULTISLICE_INFO, HAL_EXTRADATA_INDEX, HAL_EXTRADATA_NUM_CONCEALED_MB, @@ -103,6 +101,7 @@ enum hal_extradata_id { HAL_EXTRADATA_FRAME_BITS_INFO, HAL_EXTRADATA_LTR_INFO, HAL_EXTRADATA_METADATA_MBI, + HAL_EXTRADATA_STREAM_USERDATA, }; enum hal_property { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 174829da8c69..f7606728081b 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1824,29 +1824,30 @@ enum v4l2_mpeg_vidc_video_sync_frame_decode { #define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 25) enum v4l2_mpeg_vidc_extradata { - V4L2_MPEG_VIDC_EXTRADATA_NONE, - V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION, - V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO, - V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP, - V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP, - V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP, - V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING, - V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE, - V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW, - V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI, - V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD, - V4L2_MPEG_VIDC_EXTRADATA_AFD_UD, - V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO, - V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB, - V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER, - V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP, - V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM, - V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO, - V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP, - V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP, - V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO, - V4L2_MPEG_VIDC_EXTRADATA_LTR, - V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI, + V4L2_MPEG_VIDC_EXTRADATA_NONE = 0, + V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1, + V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2, + V4L2_MPEG_VIDC_EXTRADATA_VC1_FRAMEDISP = 3, + V4L2_MPEG_VIDC_EXTRADATA_VC1_SEQDISP = 4, + V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP = 5, + V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING = 6, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE = 7, + V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW = 8, + V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 9, + V4L2_MPEG_VIDC_EXTRADATA_CLOSED_CAPTION_UD = 10, + V4L2_MPEG_VIDC_EXTRADATA_AFD_UD = 11, + V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO = 12, + V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB = 13, + V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER = 14, + V4L2_MPEG_VIDC_INDEX_EXTRADATA_INPUT_CROP = 15, + V4L2_MPEG_VIDC_INDEX_EXTRADATA_DIGITAL_ZOOM = 16, + V4L2_MPEG_VIDC_INDEX_EXTRADATA_ASPECT_RATIO = 17, + V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP = 18, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP = 19, + V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO = 20, + V4L2_MPEG_VIDC_EXTRADATA_LTR = 21, + V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI = 22, + V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA = 23, }; #define V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL (V4L2_CID_MPEG_MSM_VIDC_BASE + 26) diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h index 9028b1a9dbf9..bbde6ef259b0 100644 --- a/include/media/msm_vidc.h +++ b/include/media/msm_vidc.h @@ -179,6 +179,11 @@ struct msm_vidc_frame_bits_info_payload { unsigned int header_bits; }; +struct msm_vidc_stream_userdata_payload { + unsigned int type; + unsigned int data[1]; +}; + enum msm_vidc_extradata_type { EXTRADATA_NONE = 0x00000000, EXTRADATA_MB_QUANTIZATION = 0x00000001, @@ -191,6 +196,7 @@ enum msm_vidc_extradata_type { EXTRADATA_PANSCAN_WINDOW = 0x00000008, EXTRADATA_RECOVERY_POINT_SEI = 0x00000009, EXTRADATA_MPEG2_SEQDISP = 0x0000000D, + EXTRADATA_STREAM_USERDATA = 0x0000000E, EXTRADATA_FRAME_QP = 0x0000000F, EXTRADATA_FRAME_BITS_INFO = 0x00000010, EXTRADATA_MULTISLICE_INFO = 0x7F100000, @@ -214,4 +220,9 @@ enum msm_vidc_recovery_sei { FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02, }; +enum msm_vidc_userdata_type { + MSM_VIDC_USERDATA_TYPE_FRAME = 0x1, + MSM_VIDC_USERDATA_TYPE_TOP_FIELD = 0x2, + MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3, +}; #endif From c71b1bf23f48b9cf05587fa96c96dc44c7f2d086 Mon Sep 17 00:00:00 2001 From: Deva Ramasubramanian Date: Mon, 14 Apr 2014 20:20:44 -0700 Subject: [PATCH 007/104] msm: vidc: Amend calculation of buffer sizes in VENUS_BUFFER_SIZE Add 8KB worth of padding for extradata. This is required to accommodate some of the larger extradata types that didn't fit into the residual space between the actual buffer size and its aligned size. CRs-Fixed: 647378 Change-Id: I550f806079dfbdece229f68ffdfc5c0e20b3c9e1 Signed-off-by: Deva Ramasubramanian --- include/media/msm_media_info.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h index ddf9c8eb17e7..663809eb1bea 100644 --- a/include/media/msm_media_info.h +++ b/include/media/msm_media_info.h @@ -158,8 +158,8 @@ static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height) static inline unsigned int VENUS_BUFFER_SIZE( int color_fmt, int width, int height) { - unsigned int uv_alignment; - unsigned int size = 0; + const unsigned int extra_size = 8*1024; + unsigned int uv_alignment = 0, size = 0; unsigned int y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; if (!width || !height) @@ -175,7 +175,7 @@ static inline unsigned int VENUS_BUFFER_SIZE( uv_alignment = 4096; y_plane = y_stride * y_sclines; uv_plane = uv_stride * uv_sclines + uv_alignment; - size = y_plane + uv_plane; + size = y_plane + uv_plane + extra_size; size = MSM_MEDIA_ALIGN(size, 4096); break; default: From d8b9077875316b58459962d84639908b4353eec4 Mon Sep 17 00:00:00 2001 From: Deva Ramasubramanian Date: Thu, 8 May 2014 14:47:31 -0700 Subject: [PATCH 008/104] msm: vidc: Expose extradata size to userspace Previously, the extradata size was included within VENUS_BUFFER_SIZE and callers (primarily in userspace) wouldn't know how much extra padding was added to the buffer size. Exposing it allows userspace to query directly instead of doing guesswork. Change-Id: I7f9701a4adfe364d757028514bdd4fa84402a995 Signed-off-by: Deva Ramasubramanian --- include/media/msm_media_info.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/media/msm_media_info.h b/include/media/msm_media_info.h index 663809eb1bea..d46b5051e469 100644 --- a/include/media/msm_media_info.h +++ b/include/media/msm_media_info.h @@ -79,6 +79,18 @@ enum color_fmts { COLOR_FMT_NV21, }; +static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height) +{ + (void)height; + (void)width; + + /* + * In the future, calculate the size based on the w/h but just + * hardcode it for now since 8K satisfies all current usecases. + */ + return 8 * 1024; +} + static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width) { unsigned int alignment, stride = 0; @@ -158,7 +170,7 @@ static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height) static inline unsigned int VENUS_BUFFER_SIZE( int color_fmt, int width, int height) { - const unsigned int extra_size = 8*1024; + const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height); unsigned int uv_alignment = 0, size = 0; unsigned int y_plane, uv_plane, y_stride, uv_stride, y_sclines, uv_sclines; From 0f50d2d98b4c093784c1ccdb3a82882c731c92e9 Mon Sep 17 00:00:00 2001 From: Manikanta Sivapala Date: Wed, 18 Jun 2014 17:25:45 +0530 Subject: [PATCH 009/104] msm: vidc: change concealment color 0x8080 is gray color concealment, changing it to black color, which is 0x8010. Change-Id: I50897d771913ee33a5b2c2ea486996dfc0c294bf Signed-off-by: Manikanta Sivapala Conflicts: drivers/media/platform/msm/vidc/msm_vdec.c --- drivers/media/platform/msm/vidc/msm_vdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 30c71a342017..84df7256e1fe 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -21,8 +21,8 @@ #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 -#define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME -#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8080 +#define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME +#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010 #define TZ_DYNAMIC_BUFFER_FEATURE_ID 12 #define TZ_FEATURE_VERSION(major, minor, patch) \ From 153005c398882072dd11bc035df9f1521240ba4a Mon Sep 17 00:00:00 2001 From: Praneeth Paladugu Date: Wed, 25 Sep 2013 16:25:04 -0700 Subject: [PATCH 010/104] msm: vidc: Change in input buffer size calculation Right now, input buffer size is calculated based on maximum supported height and width returned from FW. These values are not true representation as they are calculated for rotation usecase. Driver needs to use max MB supported from FW. This change fixes the same. CRs-Fixed: 599818 Change-Id: I5b5f7d0db1088a4bc16ec7a32b31e1f763d5da7c Signed-off-by: Manikanta Sivapala --- drivers/media/platform/msm/vidc/msm_vdec.c | 20 ++++++++++--------- .../media/platform/msm/vidc/msm_vidc_common.c | 2 ++ .../platform/msm/vidc/msm_vidc_internal.h | 1 + 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 84df7256e1fe..beafa4a63686 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -23,6 +23,7 @@ #define MIN_NUM_OUTPUT_BUFFERS 4 #define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME #define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010 +#define MB_SIZE_IN_PIXEL (16 * 16) #define TZ_DYNAMIC_BUFFER_FEATURE_ID 12 #define TZ_FEATURE_VERSION(major, minor, patch) \ @@ -341,9 +342,9 @@ static u32 get_frame_size_nv12(int plane, } static u32 get_frame_size_compressed(int plane, - u32 height, u32 width) + u32 max_mbs_per_frame, u32 size_per_mb) { - return (width * height * 3/2)/4; + return (max_mbs_per_frame * size_per_mb * 3/2)/2; } struct msm_vidc_format vdec_formats[] = { @@ -756,10 +757,12 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) if (plane_sizes[i] == 0) { f->fmt.pix_mp.plane_fmt[i].sizeimage = fmt->get_frame_size(i, - inst->capability.height.max, - inst->capability.width.max); + inst->capability. + mbs_per_frame.max, + MB_SIZE_IN_PIXEL); plane_sizes[i] = - f->fmt.pix_mp.plane_fmt[i].sizeimage; + f->fmt.pix_mp.plane_fmt[i]. + sizeimage; } else f->fmt.pix_mp.plane_fmt[i].sizeimage = plane_sizes[i]; @@ -1009,8 +1012,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); max_input_size = fmt->get_frame_size(0, - inst->capability.height.max, - inst->capability.width.max); + inst->capability.mbs_per_frame.max, MB_SIZE_IN_PIXEL); if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size || f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) { @@ -1111,8 +1113,8 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, *num_buffers = MIN_NUM_OUTPUT_BUFFERS; for (i = 0; i < *num_planes; i++) { sizes[i] = inst->fmts[OUTPUT_PORT]->get_frame_size( - i, inst->capability.height.max, - inst->capability.width.max); + i, inst->capability.mbs_per_frame.max, + MB_SIZE_IN_PIXEL); } property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; new_buf_count.buffer_type = HAL_BUFFER_INPUT; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 2f6d7535b966..286b19782ca2 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -485,6 +485,8 @@ static void handle_session_init_done(enum command_response cmd, void *data) inst->capability.hier_p = session_init_done->hier_p; inst->capability.pixelprocess_capabilities = call_hfi_op(hdev, get_core_capabilities); + inst->capability.mbs_per_frame = + session_init_done->mbs_per_frame; inst->capability.capability_set = true; inst->capability.buffer_mode[CAPTURE_PORT] = session_init_done->alloc_mode_out; diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index 06181dd9844e..e80167a5a4bd 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -192,6 +192,7 @@ struct msm_vidc_core_capability { struct hal_capability_supported scale_y; struct hal_capability_supported ltr_count; struct hal_capability_supported hier_p; + struct hal_capability_supported mbs_per_frame; u32 capability_set; enum buffer_mode_type buffer_mode[MAX_PORT_NUM]; }; From f3a245a880016e096933ad05a9b51e1b262701ec Mon Sep 17 00:00:00 2001 From: Manikanta Sivapala Date: Fri, 1 Aug 2014 18:57:47 +0530 Subject: [PATCH 011/104] msm: vidc: add new control for limiting i/p buffer size Take the minimum of the size calculated by driver using max width and height supported and the size set by client for input buffers. Change interface to get input and output buffer sizes. Change-Id: Ia3eb4cc7ae7bb38e2650fff1b694623e2aab62ef Signed-off-by: Manikanta Sivapala --- drivers/media/platform/msm/vidc/msm_vdec.c | 68 +++++++++++++++---- .../platform/msm/vidc/msm_vidc_internal.h | 1 + include/linux/videodev2.h | 3 + 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index beafa4a63686..be11443ca9db 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -331,6 +331,17 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { .step = 1, .cluster = 0, }, + { + .id = V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT, + .name = "Buffer size limit", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 0x7fffffff, + .default_value = 0, + .step = 1, + .menu_skip_mask = 0, + .qmenu = NULL, + }, }; #define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls) @@ -347,6 +358,36 @@ static u32 get_frame_size_compressed(int plane, return (max_mbs_per_frame * size_per_mb * 3/2)/2; } +static u32 get_frame_size(struct msm_vidc_inst *inst, + const struct msm_vidc_format *fmt, + int fmt_type, int plane) +{ + u32 frame_size = 0; + if (fmt_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.mbs_per_frame.max, + MB_SIZE_IN_PIXEL); + if (inst->capability.buffer_size_limit && + (inst->capability.buffer_size_limit < frame_size)) { + frame_size = inst->capability.buffer_size_limit; + dprintk(VIDC_DBG, "input buffer size limited to %d\n", + frame_size); + } else { + dprintk(VIDC_DBG, "set input buffer size to %d\n", + frame_size); + } + } else if (fmt_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + frame_size = fmt->get_frame_size(plane, + inst->capability.height.max, + inst->capability.width.max); + dprintk(VIDC_DBG, "set output buffer size to %d\n", + frame_size); + } else { + dprintk(VIDC_WARN, "Wrong format type\n"); + } + return frame_size; +} + struct msm_vidc_format vdec_formats[] = { { .name = "YCbCr Semiplanar 4:2:0", @@ -756,10 +797,8 @@ int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) for (i = 0; i < fmt->num_planes; ++i) { if (plane_sizes[i] == 0) { f->fmt.pix_mp.plane_fmt[i].sizeimage = - fmt->get_frame_size(i, - inst->capability. - mbs_per_frame.max, - MB_SIZE_IN_PIXEL); + get_frame_size(inst, fmt, + f->type, i); plane_sizes[i] = f->fmt.pix_mp.plane_fmt[i]. sizeimage; @@ -926,9 +965,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) if (ret) { for (i = 0; i < fmt->num_planes; ++i) { f->fmt.pix_mp.plane_fmt[i].sizeimage = - fmt->get_frame_size(i, - inst->capability.height.max, - inst->capability.width.max); + get_frame_size(inst, fmt, f->type, i); } } else { buff_req_buffer = @@ -1010,10 +1047,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f) frame_sz.buffer_type, frame_sz.width, frame_sz.height); msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz); - - max_input_size = fmt->get_frame_size(0, - inst->capability.mbs_per_frame.max, MB_SIZE_IN_PIXEL); - + max_input_size = get_frame_size(inst, fmt, f->type, 0); if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size || f->fmt.pix_mp.plane_fmt[0].sizeimage == 0) { f->fmt.pix_mp.plane_fmt[0].sizeimage = max_input_size; @@ -1112,9 +1146,8 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, *num_buffers > MAX_NUM_OUTPUT_BUFFERS) *num_buffers = MIN_NUM_OUTPUT_BUFFERS; for (i = 0; i < *num_planes; i++) { - sizes[i] = inst->fmts[OUTPUT_PORT]->get_frame_size( - i, inst->capability.mbs_per_frame.max, - MB_SIZE_IN_PIXEL); + sizes[i] = get_frame_size(inst, + inst->fmts[OUTPUT_PORT], q->type, i); } property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL; new_buf_count.buffer_type = HAL_BUFFER_INPUT; @@ -1759,6 +1792,13 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) property_val = ctrl->val; pdata = &property_val; break; + case V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT: + { + inst->capability.buffer_size_limit = ctrl->val; + dprintk(VIDC_DBG, + "Limiting input buffer size to :%u\n", ctrl->val); + break; + } default: break; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index e80167a5a4bd..a1cec43058fa 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -195,6 +195,7 @@ struct msm_vidc_core_capability { struct hal_capability_supported mbs_per_frame; u32 capability_set; enum buffer_mode_type buffer_mode[MAX_PORT_NUM]; + u32 buffer_size_limit; }; struct msm_vidc_core { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index f7606728081b..91c5f8106001 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1948,6 +1948,9 @@ enum v4l2_mpeg_vidc_video_ltrmode { #define V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP \ (V4L2_CID_MPEG_MSM_VIDC_BASE + 47) +#define V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT \ + (V4L2_CID_MPEG_MSM_VIDC_BASE + 48) + /* Camera class control IDs */ #define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900) #define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1) From 2ac2b212915364de5e0939938330624c4f187076 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 19 Nov 2014 17:28:04 +0300 Subject: [PATCH 012/104] add lge w5 d320 support --- .../msm8610-w5_global_com/msm8610-v1-w5.dts | 39 + .../msm8610-w5_global_com/msm8610-v2-w5.dts | 39 + .../msm8610-w5-camera.dtsi | 409 ++++ .../msm8610-w5-hdmi.dtsi | 15 + .../msm8610-w5-input.dtsi | 94 + .../msm8610-w5-misc.dtsi | 54 + .../msm8610-w5_global_com/msm8610-w5-nfc.dtsi | 51 + .../msm8610-w5-panel.dtsi | 45 + .../msm8610-w5_global_com/msm8610-w5-pm.dtsi | 299 +++ .../msm8610-w5-sensor.dtsi | 139 ++ .../msm8610-w5_global_com/msm8610-w5-usb.dtsi | 15 + .../dts/msm8610-w5_global_com/msm8610-w5.dtsi | 156 ++ arch/arm/configs/w5_global_com_defconfig | 523 +++++ drivers/input/touchscreen/Kconfig | 22 + drivers/input/touchscreen/Makefile | 2 + drivers/input/touchscreen/ags04_ts.c | 592 +++++ drivers/input/touchscreen/aps_ts.c | 1898 +++++++++++++++++ include/linux/input/aps_ts.h | 17 + 18 files changed, 4409 insertions(+) create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v1-w5.dts create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v2-w5.dts create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-camera.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-hdmi.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-misc.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-nfc.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-panel.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-pm.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-sensor.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-usb.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5.dtsi create mode 100644 arch/arm/configs/w5_global_com_defconfig create mode 100644 drivers/input/touchscreen/ags04_ts.c create mode 100644 drivers/input/touchscreen/aps_ts.c create mode 100644 include/linux/input/aps_ts.h diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v1-w5.dts b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v1-w5.dts new file mode 100644 index 000000000000..fb977a225f04 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v1-w5.dts @@ -0,0 +1,39 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/include/ "../msm8610-lge-common/msm8610-v1-lge.dtsi" +/include/ "msm8610-w5.dtsi" + + +/ { + model = "Qualcomm MSM 8610v1 w5globalcom"; + compatible = "qcom,msm8610-w5globalcom", "qcom,msm8610"; + /* + MSM8610 = 147, + MSM8110 = 161, + MSM8210 = 162, + MSM8810 = 163, + MSM8212 = 164, + MSM8612 = 165, + MSM8812 = 166, + */ + qcom,board-id = <122 0>; + aliases { + lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video"; + lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video"; + }; +}; + + + + diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v2-w5.dts b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v2-w5.dts new file mode 100644 index 000000000000..b118c9451fd2 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-v2-w5.dts @@ -0,0 +1,39 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/include/ "../msm8610-lge-common/msm8610-v2-lge.dtsi" +/include/ "msm8610-w5.dtsi" + + +/ { + model = "Qualcomm MSM 8610v2 w5globalcom"; + compatible = "qcom,msm8610-w5globalcom", "qcom,msm8610"; + /* + MSM8610 = 147, + MSM8110 = 161, + MSM8210 = 162, + MSM8810 = 163, + MSM8212 = 164, + MSM8612 = 165, + MSM8812 = 166, + */ + qcom,board-id = <122 0>; + aliases { + lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video"; + lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video"; + }; +}; + + + + diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-camera.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-camera.dtsi new file mode 100644 index 000000000000..0986ea0c39d9 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-camera.dtsi @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + i2c@0 { + led_flash0: qcom,led-flash@39 { + compatible = "qcom,led-flash"; + status = "ok"; + reg = <0x39>; + rt8542,lcd_bl_en = <&msmgpio 60 0x00>; + rt8542,max_current = <0x0C>; + rt8542,min_brightness = <0x04>; + rt8542,default_brightness = <0x66>; + rt8542,max_brightness = <0x7D>; + rt8542,enable_pwm = <0>; + rt8542,blmap_size = <127>; + rt8542,blmap = < + 5 5 5 5 5 5 5 5 5 5 //9 + 5 5 5 6 6 6 6 6 6 6 //19 + 6 6 6 6 6 6 7 7 7 7 //29 + 7 7 7 8 8 8 8 9 9 9 //39 + 10 10 10 11 11 11 12 12 12 12 //49 + 13 13 14 14 14 14 15 15 15 16 //59 + 17 18 19 20 20 21 22 23 23 24 //69 + 24 25 25 25 26 26 26 27 27 27 //79 + 27 28 29 30 31 32 33 34 35 36 //89 + 37 38 38 39 40 41 42 43 44 45 //99 + 46 47 48 49 50 52 53 54 55 56 //109 + 57 58 59 60 61 63 64 65 67 68 //119 + 70 71 73 75 77 79 80>; + + cell-index = <0>; + qcom,flash-name = "rt8542"; + qcom,slave-id = <0x39 0x00 0x0011>; + qcom,flash-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 18 0>; + qcom,gpio-flash-en = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "FLASH_EN"; + + }; + }; +}; + +&i2c { + +/* IMX219(8M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator2: qcom,actuator_2@18 { + cell-index = <4>; //Use Af_main_0 of dw9714 with IMX219(8M) + reg = <0x18 0x0>; + compatible = "qcom,actuator"; + }; +/* IMX111(8M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator1: qcom,actuator_1@18 { + cell-index = <1>; //Use Af_main_0 of dw9714 with IMX111(8M) + reg = <0x19 0x0>; + compatible = "qcom,actuator"; +}; +/* HI543(5M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator0: qcom,actuator_0@18 { + cell-index = <0>; //Use Af_main_0 of dw9716 with HI543(5M) + reg = <0x1A 0x0>; + compatible = "qcom,actuator"; + }; + +/* IMX111(8M) eeprom */ + eeprom1: msm_eeprom_imx111@50 { //EEPROM READ + cell-index = <0>; + reg = <0x50 0x0>; + qcom,eeprom-name = "imx111_eeprom"; + compatible = "msm_eeprom"; + qcom,slave-addr = <0x50>; + + qcom,num-blocks = <4>; + qcom,page0 = <0 0x0 1 0x0 1 20>; // valid size, addr, addr_t, data, data_t, delay + qcom,poll0 = <0 0x0 1 0x0 1 20>; + qcom,mem0 = <0x100 0x00 1 0 1 0>; //EEPROM READ + + qcom,page1 = <0 0x0 1 0x0 1 20>; + qcom,poll1 = <0 0x0 1 0x0 1 20>; + qcom,mem1 = <0x100 0x00 1 0 1 0>; + + qcom,page2 = <0 0x0 1 0x0 1 20>; + qcom,poll2 = <0 0x0 1 0x0 1 20>; + qcom,mem2 = <0x100 0x00 1 0 1 0>; + + qcom,page3 = <0 0x0 1 0x0 1 20>; + qcom,poll3 = <0 0x0 1 0x0 1 20>; + qcom,mem3 = <0xC6 0x00 1 0 1 0>; + + cam_vio-supply = <&pm8110_l7>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-type = <0>; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 20 0>; //2.8V VCM + qcom,gpio-standby = <0>; //CAM_STANDBY + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_STANDBY"; + + qcom,cam-power-seq-type = "sensor_vreg","sensor_gpio"; + qcom,cam-power-seq-val = "cam_vio","sensor_gpio_standby"; + qcom,cam-power-seq-cfg-val = <1 1>; + qcom,cam-power-seq-delay = <1 1>; + }; +/* HI543(5M) eeprom */ + eeprom0: msm_eeprom_hi543@14 { //EEPROM READ + cell-index = <0>; + reg = <0x14 0x0>; + qcom,eeprom-name = "hi543_eeprom"; + compatible = "msm_eeprom"; + qcom,slave-addr = <0x14>; + + qcom,num-blocks = <1>; + qcom,page0 = <0 0x0 1 0x0 1 20>; // valid size, addr, addr_t, data, data_t, delay + qcom,poll0 = <0 0x0 1 0x0 1 20>; + qcom,mem0 = <0x06 0x0000 2 0 1 0>; //EEPROM READ + + cam_vio-supply = <&pm8110_l7>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-type = <0>; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 20 0>; //2.8V VCM + qcom,gpio-standby = <0>; //CAM_STANDBY + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_STANDBY"; + + qcom,cam-power-seq-type = "sensor_vreg","sensor_gpio"; + qcom,cam-power-seq-val = "cam_vio","sensor_gpio_standby"; + qcom,cam-power-seq-cfg-val = <1 1>; + qcom,cam-power-seq-delay = <1 1>; + }; + +/* Rev a */ + hi543a: qcom,camera_rev_a@40 { + compatible = "qcom,hi543"; + reg = <0x40 0x0>; + qcom,slave-id = <0x40 0x0 0x1F3C>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator0>; + qcom,mount-angle = <90>; + qcom,sensor-name = "hi543"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 8000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 91 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_a"; + }; + + hi707a: qcom,camera_rev_a@60 { + compatible = "qcom,hi707"; + reg = <0x60>; + qcom,slave-id = <0x60 0x4 0xB8>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <270>; + qcom,sensor-name = "hi707"; + + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 8000 80000>; + qcom,sensor-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 14 0>, + <&msmgpio 15 0>, //VT_CAM_RESET_N, GPIO 15 + <&msmgpio 67 0>, //VT_CAM_PWDN, GPIO=67 + <&msmgpio 91 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", "CAM_STANDBY2", "MAIN_VANA_EN2"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x3>; + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + status = "okay"; + revision = "rev_a"; + }; +/* Rev B ~ */ + hi543: qcom,camera_rev_b@40 { + compatible = "qcom,hi543"; + reg = <0x40 0x0>; + qcom,slave-id = <0x40 0x0 0x1F3C>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator0>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom0>; + qcom,mount-angle = <90>; + qcom,sensor-name = "hi543"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "decide_in_lk"; + revision = "rev_b..."; + }; + + hi707: qcom,camera_rev_b@60 { + compatible = "qcom,hi707"; + reg = <0x60>; + qcom,slave-id = <0x60 0x4 0xB8>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <270>; + qcom,sensor-name = "hi707"; + qcom,maker-gpio = <86>; + + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + qcom,sensor-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 14 0>, + <&msmgpio 15 0>, //VT_CAM_RESET_N, GPIO 15 + <&msmgpio 67 0>, //VT_CAM_PWDN, GPIO=67 + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", "CAM_STANDBY2", "MAIN_VANA_EN2"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x3>; + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + status = "okay"; + revision = "rev_b..."; + }; +/* imx111 Rev B ~ */ + imx111: qcom,camera_rev_b@20 { + compatible = "qcom,imx111"; + reg = <0x20 0x0>; + qcom,slave-id = <0x20 0x0 0x111>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator1>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom1>; + qcom,mount-angle = <90>; + qcom,sensor-name = "imx111"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_b..."; + }; + + imx219: qcom,camera_rev_b@34 { + compatible = "qcom,imx219"; + reg = <0x34 0x0>; + qcom,slave-id = <0x34 0x0 0x219>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator2>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom1>; + qcom,mount-angle = <90>; + qcom,sensor-name = "imx219"; + cam_vdig-supply = <&pm8110_l7>; //CAM_DVDD_1V8_1V2 + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio";//, "cam_vana"; + qcom,cam-vreg-type = <0 0>; + qcom,cam-vreg-min-voltage = <1200000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1200000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, //MAIN_CAM_MCLK + <&msmgpio 21 0>, //MAIN_RESET_N + <&msmgpio 20 0>, //VCM_PWDN, //CAM_VCM_2V8, MAIN_CAM0_VCM_PWDN=GPIO36 + <&msmgpio 85 0>; //CAM_AVDD_2V8, LDO1_EN=GPIO 62 + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; // +VCM_2V8 (AF) + qcom,gpio-vana = <3>; // =CAM_AVDD_2V8 + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x07>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_b..."; + }; +}; + +/ { + aliases { + hi543 = &hi543; /* 5M Main Camera */ + imx111 = &imx111; /* 8M Main Camera */ + imx219 = &imx219; /* 8M Main Camera */ + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-hdmi.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-hdmi.dtsi new file mode 100644 index 000000000000..030f0d89cc42 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-hdmi.dtsi @@ -0,0 +1,15 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi new file mode 100644 index 000000000000..de6d3483f98d --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi @@ -0,0 +1,94 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + i2c@f9923000{ + mms100s@48 { + melfas,product = "I2S45B"; + melfas,max-x = <480>; + melfas,max-y = <800>; + melfas,use_vdd_i2c = <1>; + melfas,gpio-vdd-en = <62>; + melfas,key-map = <158 139>; + melfas,fw-image-name = "melfas/w5_global_com/w5_global_com_rev_b_v101.mfsb"; + status = "ok"; + revision = "rev_b"; + }; + + mms100s@48_rev_c { + melfas,product = "I2S45B"; + melfas,max-x = <480>; + melfas,max-y = <800>; + melfas,use_vdd_i2c = <1>; + melfas,gpio-vdd-en = <82>; + melfas,key-map = <158 139>; + melfas,fw-image-name = "melfas/w5_global_com/w5_global_com_rev_b_v101.mfsb"; + status = "ok"; + revision = "rev_c"; + }; + + lgd_melfas@34 { + status = "ok"; + revision = "rev_a"; + }; + + ads_ags04@6a { + status = "ok"; + revision = "rev_a"; + }; + + synaptics_red@20 { + status = "disable"; + revision = "rev_d..."; + synaptics,button-map = <158 139>; + synaptics,gpio_vdd_en = <82>; + }; + + synaptics_s220x@20 { + status = "ok"; + revision = "rev_d..."; + synaptics,fw_version_info = <0x82 0x04 0x51>; + synaptics,fw_image = "synaptics/w5_global_com/PLG389-V1.10-PR1600582-DS4.3.5.1.16_1204518A.img", /* panel id 0 */ + "synaptics/w5_global_com/PLG317-V1.14-PR1600582-DS4.3.5.1.16_3204518E.img"; /* panel id 1 */ + synaptics,panel_spec = "synaptics/w5_global_com/w5_Suntel_limit.txt", /* panel id 0 */ + "synaptics/w5_global_com/w5_Inotek_limit.txt";/* panel id 1 */ + synaptics,global_access_pixel = <10>; + lge,knock_on_type = <1>; + synaptics,platform_data { + number_of_button = <2>; + button_name = [9e 8b]; + x_max = <960>; + y_max = <1600>; + lcd_x = <480>; + lcd_y = <800>; + use_vio_regulator = <1>; + gpio_vdd_en = <82>; + /*maker_id disable(0), enable(1)*/ + maker_id = <1>; + maker_id_gpio = <77>; + palm_detect_mode = <1>; + ghost_detection_enable = <0>; + ghost_detection_value = <200 5 4 6 5 5 10 500 100 6 5>; + }; + }; + }; + gpio_keys { + quick_clip { + label = "quick_clip"; + gpios = <&msmgpio 75 0x1>; + linux,input-type = <1>; + linux,code = <250>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-misc.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-misc.dtsi new file mode 100644 index 000000000000..72634a2d6351 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-misc.dtsi @@ -0,0 +1,54 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + earjack-debugger { + status = "disable"; + }; + + i2c@0 { + rt8542@39 { + status = "ok"; + rt8542,max_current = <0x0C>; /* 12.1mA */ + rt8542,max_brightness = <105>; /* 105 */ + rt8542,blmap = < + 10 10 12 12 12 14 14 16 16 18 + 18 19 19 20 20 22 22 22 24 24 + 26 26 28 28 29 29 30 30 32 32 + 32 34 34 36 36 38 38 39 39 40 + 40 42 42 42 44 44 46 46 48 48 + 49 49 50 50 52 52 52 54 54 56 + 56 58 58 59 59 60 60 62 62 62 + 64 64 66 66 68 68 69 69 70 70 + 72 72 72 74 74 76 76 78 78 79 + 79 80 80 82 82 82 84 84 86 86 + 88 88 89 89 90 90 92 92 92 94 + 94 96 96 96 98 98 99 99 100 100 + 102 102 104 104 105 105 105>; + }; + lp5521@32{ + status = "no"; + revision = "rev_a..."; + ti,led_en = <&msmgpio 6 0x0>; + ti,i2c-pull-up = <1>; + ti,vddio_i2c-supply = <&pm8110_l14>; + ti,vddio_i2c_supply_min = <1800000>; + ti,vddio_i2c_supply_max = <1800000>; + ti,vddio_i2c_load_ua = <10000>; + + }; + }; + + hall-bu52061nvx { + status = "ok"; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-nfc.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-nfc.dtsi new file mode 100644 index 000000000000..5af622b8f6e7 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-nfc.dtsi @@ -0,0 +1,51 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* LGE_CHANGE, [NFC][garam.kim@lge.com], NFC Bring up temp */ +&soc { + + i2c@0 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 4 0x00>; + nxp,gpio_scl = <&msmgpio 5 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_b"; + }; + }; +/* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up temp */ + i2c@f9926000 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 88 0x00>; + nxp,gpio_scl = <&msmgpio 89 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_c..."; + }; + }; + + +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-panel.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-panel.dtsi new file mode 100644 index 000000000000..84ef2af28ec9 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-panel.dtsi @@ -0,0 +1,45 @@ +/* Copyright (c) 2013, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_lgd_lg4577_wvga_video>; + qcom,dsi-pref-secondary-pan = <&dsi_tovis_shrink_wvga_video>; + qcom,platform-supply-entry3 { + qcom,supply-name = "vdda"; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <5>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; +}; + +&dsi_tovis_shrink_wvga_video { + qcom,cont-splash-enabled; + status = "disable"; + revision = "rev_b..."; +}; + +&dsi_lgd_lg4577_wvga_video { + qcom,cont-splash-enabled; + status = "disable"; + revision = "rev_a"; +}; + diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-pm.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-pm.dtsi new file mode 100644 index 000000000000..337f6ce304df --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-pm.dtsi @@ -0,0 +1,299 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +/ { + +}; + +&spmi_bus { + qcom,pm8110@0 { + qcom,leds@a100 { + status = "okay"; + qcom,led_mpp_2 { + label = "mpp"; + linux,name = "button-backlight"; + linux-default-trigger = "hr-trigger"; + qcom,default-state = "off"; + qcom,max-current = <40>; + qcom,id = <6>; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x60>; + qcom,mode = "manual"; + }; + }; + + qcom,leds@a200 { + status = "disabled"; + qcom,led_mpp_3 { + label = "mpp"; + linux,name = "wled-backlight"; + linux,default-trigger = "bkl-trigger"; + qcom,default-state = "on"; + qcom,max-current = <40>; + qcom,id = <6>; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x10>; + qcom,mode = "manual"; + }; + }; + }; +}; + +&spmi_bus { + qcom,pm8110@1 { + qcom,vibrator@c000 { + status = "okay"; + qcom,vib-timeout-ms = <15000>; + qcom,vib-vtg-level-mV = <3000>; + }; + }; +}; + +&soc{ + /* Cable type definition. + * Modify the USB cable and move to SoC + * to get each table for different USB Pull up resistor via HW Rev + * This must have the compatible property "lge,usb-id". + * If you don't want to use revision you can remove status & revision property. + * If this node is not exist, the charger driver would not be probed. + * Each cable type has 3 values + * that we can configure the threshold of ADC value and TA/USB IUSB max current value. + * ex) + * cable type = . + */ + + lge,usb_cable@0 { + compatible = "lge,usb-id"; + usb_adc_table@665{ + status = "okay"; + revision = "rev_0"; + lge,no-init-cable = <100000 500 500>; + lge,cable-56k = <200000 1500 1500>; + lge,cable-100k = <239000 500 500>; + lge,cable-130k = <340000 1500 1500>; + lge,cable-180k = <400000 800 500>; + lge,cable-200k = <430000 800 500>; + lge,cable-220k = <485000 800 500>; + lge,cable-270k = <560000 800 500>; + lge,cable-330k = <735000 500 500>; + lge,cable-620k = <955000 500 500>; + lge,cable-910k = <1140000 1500 1500>; + lge,cable-none = <1900000 800 500>; + }; + usb_adc_table@200{ + status = "okay"; + revision = "rev_a..."; + lge,no-init-cable = <197000 500 500>; + lge,cable-56k = <496875 1500 1500>; + lge,cable-100k = <654545 500 500>; + lge,cable-130k = <780861 1500 1500>; + lge,cable-180k = <876316 800 500>; + lge,cable-200k = <921429 800 500>; + lge,cable-220k = <988450 800 500>; + lge,cable-270k = <1077399 800 500>; + lge,cable-330k = <1240865 500 500>; + lge,cable-620k = <1418326 500 500>; + lge,cable-910k = <1637838 1500 1500>; + lge,cable-none = <1900000 800 500>; + }; + }; +}; + +&pm8110_chg { + status = "ok"; + qcom,vddmax-mv = <4350>; + qcom,vddsafe-mv = <4380>; + qcom,vbatdet-mv = <4300>; + qcom,vinmin-mv = <4350>; + qcom,ibatterm-ma = <100>; + qcom,vbatdet-delta-mv = <100>; + qcom,tchg-mins = <480>; + qcom,duty-cycle-100p = <1>; + + qcom,chgr@1000 { + status = "ok"; + }; + + qcom,buck@1100 { + status = "ok"; + }; + + qcom,bat-if@1200 { + status = "ok"; + }; + + qcom,usb-chgpth@1300 { + status = "ok"; + }; + + qcom,chg-misc@1600 { + status = "ok"; + }; +}; + +&pm8110_gpios { + gpio@c000 { /* GPIO 1 */ /* NFC_CLK_REQ */ + }; + + gpio@c100 { /* GPIO 2 */ /* TX_GTR_THRESH */ + qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */ + qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */ + qcom,pull = <5>; /* QPNP_PIN_PULL_NO */ + qcom,vin-sel = <0>; /* QPNP_PIN_VIN0 */ + qcom,out-strength = <1>; /* QPNP_PIN_OUT_STRENGTH_LOW */ + qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; /* Eable GPIO */ + }; + + gpio@c200 { /* GPIO 3 */ /* NC */ + }; + + gpio@c300 { /* GPIO 4 */ /* BAT_UICC_ALARM */ + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + /* GPIO 59 : EAR_MIC_EN */ + gpio@fa00 { + status = "ok"; + qcom,mode = <1>; + qcom,output-type = <0>; + qcom,pull = <1>; /* set to default pull */ + qcom,out-strength = <2>; + com,vin-sel = <2>; /* select 1.8 V source */ + qcom,src-select = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ +}; + +&pm8110_mpps { + mpp@a000 { /* MPP 1 */ /* VDD_PX_BIAS */ + }; + + mpp@a100 { /* MPP 2 */ /* VPWR_KEY_LED */ +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + reg = <0xa100 0x100>; + qcom,pin-num = <2>; + + status = "ok"; + qcom,mode = <4>; /* QPNP_PIN_MODE_AIN */ + qcom,invert = <0>; /*no invert*/ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <2>; /* PM8226_S3 1.8V > 1.6V */ + qcom,src-sel = <0>; /* CONSTANT */ + qcom,ain-route = <1>; + qcom,master-en = <1>; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ + }; + + mpp@a200 { /* MPP 3 */ /* USB_ID */ + qcom,mode = <4>; /* QPNP_PIN_MODE_AIN */ + qcom,invert = <1>; /* QPNP_PIN_INVERT_ENABLE - Enable MPP */ + qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; /* Eable GPIO */ + qcom,ain-route = <2>; /* QPNP_PIN_AIN_AMUX_CH7 */ + }; + + mpp@a300 { /* MPP 4 */ /* PA_THERM */ + /* PA_THERM config */ + qcom,mode = <4>; /* AIN input */ + qcom,invert = <1>; /* Enable MPP */ + qcom,ain-route = <3>; /* AMUX 8 */ + qcom,master-en = <1>; + qcom,src-sel = <0>; /* Function constant */ + }; +}; + +&pm8110_bms { + status = "ok"; + + qcom,r-sense-uohm = <10000>; + qcom,v-cutoff-uv = <3400000>; + qcom,max-voltage-uv = <4350000>; + qcom,r-conn-mohm = <18>; + qcom,shutdown-soc-valid-limit = <20>; + qcom,adjust-soc-low-threshold = <25>; + qcom,adjust-soc-high-threshold = <45>; + qcom,ocv-voltage-high-threshold-uv = <3900000>; + qcom,ocv-voltage-low-threshold-uv = <3650000>; + qcom,low-soc-calculate-soc-threshold = <15>; + qcom,low-soc-calculate-soc-ms = <5000>; + qcom,calculate-soc-ms = <20000>; + qcom,low-voltage-calculate-soc-ms=<1000>; + qcom,chg-term-ua = <100000>; + qcom,batt-type = <0>; + qcom,low-voltage-threshold = <3420000>; +}; + +&pm8110_vadc { + +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + chan@11 { + status = "ok"; + label = "mpp2_adc"; + reg = <0x11>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ + + chan@12 { + label = "mpp3_usb_id"; + reg = <0x12>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@13 { + label = "pa_therm0"; + reg = <0x13>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; /* degC for 100k pull-up */ + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@33 { + label = "hw_id_pcb_revision"; + reg = <0x33>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + +}; + +&rpm_bus { + + rpm-regulator-ldoa7 { + status = "okay"; + pm8110_l7: regulator-l7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; + }; + }; +}; + diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-sensor.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-sensor.dtsi new file mode 100644 index 000000000000..dbcd78e1427f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-sensor.dtsi @@ -0,0 +1,139 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + /* I2C device */ + i2c@f9925000 { + /* Acceleration sensor */ + + K303C_ACC@1D { + revision = "rev_a"; + status = "okay"; + compatible = "STMicroelectronics,accelerometer" , "STMicroelectronics,K303C" ; + reg = <0x1D>; + interrupt-parent = <&msmgpio>; + interrupts = <81 0x2>; + vdda-supply = <&pm8110_l19>; + vddio-supply = <&pm8110_l14>; + STMicroelectronics,irq-gpio = <&msmgpio 81 0x00>; + STMicroelectronics,i2c-pull-up = <1>; + STMicroelectronics,vdd_ana_supply_min = <2850000>; + STMicroelectronics,vdd_ana_supply_max = <3300000>; + STMicroelectronics,vdd_ana_load_ua = <15000>; + STMicroelectronics,vddio_dig_supply_min = <1800000>; + STMicroelectronics,vddio_dig_supply_max = <1800000>; + STMicroelectronics,vddio_dig_load_ua = <10000>; + STMicroelectronics,vddio_i2c_supply_min = <1800000>; + STMicroelectronics,vddio_i2c_supply_max = <1800000>; + STMicroelectronics,vddio_i2c_load_ua = <10000>; + }; + + + Bosch_bma2x2@10 { + compatible = "Bosch,bma2x2"; + reg = <0x10>; + interrupt-parent = <&msmgpio>; + interrupts = <81 0x2>; + Bosch,vdd_ana-supply = <&pm8110_l19>; + Bosch,vddio_i2c-supply = <&pm8110_l14>; + Bosch,irq-gpio = <&msmgpio 81 0x00>; + Bosch,i2c-pull-up = <1>; + Bosch,vdd_ana_supply_min = <2850000>; + Bosch,vdd_ana_supply_max = <3300000>; + Bosch,vdd_ana_load_ua = <15000>; + Bosch,vddio_dig_supply_min = <1800000>; + Bosch,vddio_dig_supply_max = <1800000>; + Bosch,vddio_dig_load_ua = <10000>; + Bosch,vddio_i2c_supply_min = <1800000>; + Bosch,vddio_i2c_supply_max = <1800000>; + Bosch,vddio_i2c_load_ua = <10000>; + place = <4>; + status = "ok"; + revision = "rev_b..."; + }; + + /* Magnetic Sensor Driver */ + K303C_MAG@1E { + revision = "rev_a"; + status = "okay"; + compatible = "STMicroelectronics,magnetometer" , "STMicroelectronics,K303C"; + reg = <0x1E>; + interrupt-parent = <&msmgpio>; + interrupts = <83 0x2>; + vdda-supply = <&pm8110_l19>; + vddio-supply = <&pm8110_l14>; + STMicroelectronics,irq-gpio = <&msmgpio 83 0x00>; + STMicroelectronics,i2c-pull-up = <1>; + STMicroelectronics,vdd_ana_supply_min = <2850000>; + STMicroelectronics,vdd_ana_supply_max = <3300000>; + STMicroelectronics,vdd_ana_load_ua = <15000>; + STMicroelectronics,vddio_dig_supply_min = <1800000>; + STMicroelectronics,vddio_dig_supply_max = <1800000>; + STMicroelectronics,vddio_dig_load_ua = <10000>; + STMicroelectronics,vddio_i2c_supply_min = <1800000>; + STMicroelectronics,vddio_i2c_supply_max = <1800000>; + STMicroelectronics,vddio_i2c_load_ua = <10000>; + + }; + + Bosch_bmm050@12 { + compatible = "Bosch,bmm050"; + reg = <0x12>; + interrupt-parent = <&msmgpio>; + interrupts = <83 0x2>; + Bosch,vdd_ana-supply = <&pm8110_l19>; + Bosch,vddio_i2c-supply = <&pm8110_l14>; + Bosch,irq-gpio = <&msmgpio 83 0x00>; + Bosch,i2c-pull-up = <1>; + Bosch,vdd_ana_supply_min = <2850000>; + Bosch,vdd_ana_supply_max = <3300000>; + Bosch,vdd_ana_load_ua = <15000>; + Bosch,vddio_dig_supply_min = <1800000>; + Bosch,vddio_dig_supply_max = <1800000>; + Bosch,vddio_dig_load_ua = <10000>; + Bosch,vddio_i2c_supply_min = <1800000>; + Bosch,vddio_i2c_supply_max = <1800000>; + Bosch,vddio_i2c_load_ua = <10000>; + place = <4>; + status = "ok"; + revision = "rev_b..."; + }; + + /* Proximity sensor */ + Avago_apds9130@39 { + compatible = "Avago,apds9130"; + reg = <0x39>; + interrupt-parent = <&msmgpio>; + interrupts = <80 0x2>; + Avago,vdd_ana-supply = <&pm8110_l19>; + Avago,vddio_i2c-supply = <&pm8110_l14>; + Avago,irq-gpio = <&msmgpio 80 0x00>; + Avago,i2c-pull-up = <1>; + Avago,vdd_ana_supply_min = <2850000>; + Avago,vdd_ana_supply_max = <3300000>; + Avago,vdd_ana_load_ua = <15000>; + Avago,vddio_dig_supply_min = <1800000>; + Avago,vddio_dig_supply_max = <1800000>; + Avago,vddio_dig_load_ua = <10000>; + Avago,vddio_i2c_supply_min = <1800000>; + Avago,vddio_i2c_supply_max = <1800000>; + Avago,vddio_i2c_load_ua = <10000>; + /*add*/ + Avago,ppcount = <8>; + Avago,pdrive = <0>; + Avago,near_offset = <150>; + Avago,far_offset = <50>; + Avago,crosstalk_max = <770>; + status = "ok"; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-usb.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-usb.dtsi new file mode 100644 index 000000000000..44d938008f8f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-usb.dtsi @@ -0,0 +1,15 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +/ { + +}; diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5.dtsi new file mode 100644 index 000000000000..c15ae5ad6d0a --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5.dtsi @@ -0,0 +1,156 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "msm8610-w5-panel.dtsi" +/include/ "msm8610-w5-sensor.dtsi" +/include/ "msm8610-w5-hdmi.dtsi" +/include/ "msm8610-w5-usb.dtsi" +/include/ "msm8610-w5-input.dtsi" +/include/ "msm8610-w5-misc.dtsi" +/include/ "msm8610-w5-pm.dtsi" +/include/ "msm8610-w5-camera.dtsi" +/include/ "msm8610-w5-nfc.dtsi" /* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up */ + +&soc { + serial@f991f000 { + status = "disabled"; + }; + + maxim_max1462x_rev0 { //LGE_UPDATE 20130626 beekay.lee@lge.com WX_MAXIM + compatible = "maxim,max1462x"; + status = "ok"; //"ok" or "disable" + revision = "rev_0"; + }; + + maxim_max1462x_revA { + compatible = "maxim,max1462x"; + status = "ok"; //"ok" or "disable" + revision = "rev_a"; + }; + + maxim_max1462x_revB { + compatible = "maxim,max1462x"; + status = "disable"; //"ok" or "disable" + revision = "rev_b"; + }; + + maxim_max1462x_revC { + compatible = "maxim,max1462x"; + status = "disable"; //"ok" or "disable" + revision = "rev_c..."; + }; + + i2c@f9924000 { /* BLSP-1 QUP-2 */ + cell-index = <2>; + compatible = "qcom,i2c-qup"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0xf9924000 0x1000>; + interrupt-names = "qup_err_intr"; + interrupts = <0 96 0>; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <19200000>; + qcom,sda-gpio = <&msmgpio 6 0>; + qcom,scl-gpio = <&msmgpio 7 0>; + status = "disable"; + }; + + i2c@f9925000 { /* BLSP-1 QUP-3 */ + cell-index = <3>; + qcom,i2c-src-freq = <19200000>; + qcom,sda-gpio = <&msmgpio 10 0>; + qcom,scl-gpio = <&msmgpio 11 0>; + }; + + i2c@0 { + compatible = "i2c-gpio"; + gpios = <&msmgpio 4 0 /* sda */ + &msmgpio 5 0 /* scl */ + >; + #address-cells = <1>; + #size-cells = <0>; + i2c-gpio,delay-us = <2>; + }; + i2c@f9926000 { /* BLSP1 QUP4 */ + cell-index = <4>; + compatible = "qcom,i2c-qup"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0xf9926000 0x1000>; + interrupt-names = "qup_err_intr"; + interrupts = <0 98 0>; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <19200000>; + revision = "rev_c..."; + }; +}; + +&sdhc_1 { + vdd-supply = <&pm8110_l17>; + qcom,vdd-always-on; + qcom,vdd-lpm-sup; + qcom,vdd-voltage-level = <2900000 2900000>; + qcom,vdd-current-level = <200 400000>; + + vdd-io-supply = <&pm8110_l6>; + qcom,vdd-io-always-on; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 60000>; + + qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */ + qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ + + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v"; + qcom,nonremovable; + + status = "ok"; +}; + +&sdhc_2 { + vdd-supply = <&pm8110_l18>; + qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-current-level = <15000 400000>; + + vdd-io-supply = <&pm8110_l21>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <200 50000>; + + qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */ + qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ + + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &msmgpio 42 0x3>; + interrupt-names = "hc_irq", "pwr_irq", "status_irq"; + cd-gpios = <&msmgpio 42 0x0>; /* card detect 1:Low-Active, 0:High-Active */ + + status = "ok"; +}; +/* this memory reservation setting for LG logo in booting time*/ +&mdss_fb0 { + qcom,memory-reservation-type = "EBI1"; + qcom,memory-reservation-size = <0x300000>; +}; diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig new file mode 100644 index 000000000000..658167157081 --- /dev/null +++ b/arch/arm/configs/w5_global_com_defconfig @@ -0,0 +1,523 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_AUDIT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_IKCONFIG=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_IOSCHED_TEST=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8610=y +CONFIG_MACH_MSM8X10_W5=y +CONFIG_MACH_MSM8X10_W5_GLOBAL_COM=y +CONFIG_LGE_BOOTLOADER_LOG=y +CONFIG_LGE_HANDLE_PANIC=y +CONFIG_LGE_BOOTLOADER_TIME_CHECKER=y +CONFIG_LGE_USE_CPU_CLOCK_TIMESTAMP=y +CONFIG_LGE_QFPROM_INTERFACE=y +CONFIG_LGE_PM_BATTERY_CAPACITY_2100mAh=y +CONFIG_LGE_CRASH_FOOTPRINT=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_BAM_DMUX=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_IPC_LOGGING=y +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y +CONFIG_MSM_PIL_LPASS_QDSP6V5=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_MSM_PIL_VENUS=y +CONFIG_MSM_PIL_PRONTO=y +CONFIG_MSM_BUSPM_DEV=m +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_DIRECT_SCLK_ACCESS=y +CONFIG_MSM_WATCHDOG_V2=y +CONFIG_MSM_MEMORY_DUMP=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_OCMEM=y +CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y +CONFIG_MSM_OCMEM_DEBUG=y +CONFIG_MSM_OCMEM_NONSECURE=y +CONFIG_MSM_OCMEM_POWER_DISABLE=y +CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_COMPACTION=y +CONFIG_KSM=y +CONFIG_CP_ACCESS=y +CONFIG_USE_OF=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFB=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_DRR=y +CONFIG_NET_SCH_MQPRIO=y +CONFIG_NET_SCH_CHOKE=y +CONFIG_NET_SCH_QFQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_ACT=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCISMD=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +CONFIG_LGE_NFC=y +CONFIG_LGE_NFC_PN547=y +CONFIG_LGE_NFC_SET_IRQ_WAKEUP=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=4 +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_UID_STAT=y +CONFIG_TSPDRV=y +CONFIG_TSPDRV_3_0V_VIBRATOR=y +CONFIG_QSEECOM=y +CONFIG_BU52061NVX=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_TUN=y +# CONFIG_MSM_RMNET is not set +CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_WCNSS_CORE=y +CONFIG_WCNSS_CORE_PRONTO=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_SENSOR=y +CONFIG_SENSORS_BMA2X2=y +CONFIG_SENSORS_BMM050=y +CONFIG_SENSOR_APDS9130=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GEN_VKEYS=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y +CONFIG_TOUCHSCREEN_APS_MELFAS=y +CONFIG_TOUCHSCREEN_ADS_AGS04=y +CONFIG_LGE_TOUCHSCREEN_SYNAPTIC=y +CONFIG_TOUCHSCREEN_MELFAS_MMS100S=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=m +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_N_HDLC=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_LGE_DEBUG_UART=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB=y +CONFIG_MSM_QPNP_INT=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_SUPPLY=y +CONFIG_QPNP_CHARGER=y +CONFIG_BATTERY_BCL=y +CONFIG_QPNP_BMS=y +CONFIG_LGE_PM=y +CONFIG_LGE_PM_BATTERY_ID_CHECKER=y +CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY=y +CONFIG_LGE_PM_FACTORY_TESTMODE=y +CONFIG_LGE_PM_USB_ID=y +CONFIG_LGE_PM_CHARGING_CHARGERLOGO=y +CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO=y +CONFIG_LGE_PM_BATTERY_PROFILE_DATA=y +CONFIG_LGE_PM_BATTERY_SOC_RESCALING=y +CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME=y +CONFIG_LGE_PM_BMS_VERY_LOW_CAL_TIME=y +CONFIG_LGE_PM_NEED_TO_MONITORING_QCT_PATCH=y +CONFIG_LGE_PM_4_25V_CHARGING_START=y +CONFIG_LGE_PM_SMPL_COUNT=y +CONFIG_LGE_PM_THERMAL=y +CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO=y +CONFIG_LGE_PM_WORKAROUND_PORT_OPEN_FAIL_IN_FACTORY_TEST=y +CONFIG_LGE_PM_BMS_FACTORY_TEST=y +CONFIG_LGE_PM_BMS_CC_ACCURACY_PATCH=y +CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_SENSORS_QPNP_ADC_CURRENT=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_QPNP=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_WCD9306_CODEC=y +CONFIG_REGULATOR_STUB=y +CONFIG_REGULATOR_QPNP=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_MSM_CAMERA is not set +CONFIG_MSMB_CAMERA=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_MSM_ISPIF_V1=y +CONFIG_IMX111=y +CONFIG_IMX219=y +CONFIG_HI543=y +CONFIG_HI707=y +CONFIG_BACKLIGHT_RT8542=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_WFD=y +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_RADIO_IRIS=y +CONFIG_RADIO_IRIS_TRANSPORT=m +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8X10=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_DEBUG_FS=y +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_G_ANDROID=y +CONFIG_LAF_G_DRIVER=y +CONFIG_USB_G_LGE_ANDROID_PFSC=y +CONFIG_USB_G_LGE_ANDROID_AUTORUN=y +CONFIG_USB_G_LGE_ANDROID_AUTORUN_LGE=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_LGE_REINIT_SDCARD_FOR_DETECT_FAIL=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_MSM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_MSM_SPS_SUPPORT=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_SWITCH=y +CONFIG_SWITCH_MAX1462X=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_QPNP=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ZRAM=y +CONFIG_ZSMALLOC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_LOGCAT_SIZE=64 +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_PRONTO_WLAN=y +CONFIG_PRIMA_WLAN_LFR=y +CONFIG_PRIMA_WLAN_OKC=y +CONFIG_PRIMA_WLAN_11AC_HIGH_TP=y +CONFIG_WLAN_FEATURE_11W=y +CONFIG_QCOM_VOWIFI_11R=y +CONFIG_SPS=y +CONFIG_USB_BAM=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_QPNP_PWM=y +CONFIG_QPNP_POWER_ON=y +CONFIG_QPNP_VIBRATOR=y +CONFIG_QPNP_REVID=y +CONFIG_MSM_IOMMU_V0=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_ECRYPT_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_KEYS=y +CONFIG_ENCRYPTED_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_HW is not set +CONFIG_LGE_DM_APP=y +CONFIG_LGE_DM_DEV=y diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b94f3aae7a79..84b342037899 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -1109,6 +1109,28 @@ config TOUCHSCREEN_GT9XX If unsure, say N. +config TOUCHSCREEN_APS_MELFAS + tristate "LGDisplay touchscreen with melfas" + depends on I2C + help + Say Y here to enable support for Melfas Touch driver + series touchscreen controller. + + If unsure, say N. + + To compile this driver as module, choose M here: the + module will be called ldctc + +config TOUCHSCREEN_ADS_AGS04 + tristate "AD-Semiconductor AGS04 touch sensor driver" + depends on I2C + default n + help + Say Y here to enable support for + AD-Semiconductor touchsensor controller. + + If unsure, say N. + source "drivers/input/touchscreen/gt9xx/Kconfig" endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 0011d421210d..68c11f61ec7b 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -100,3 +100,5 @@ obj-$(CONFIG_LGE_TOUCH_SYNAPTICS) += lge_touch_core.o obj-$(CONFIG_LGE_TOUCH_SYNAPTICS) += touch_synaptics.o obj-$(CONFIG_LGE_TOUCH_SYNAPTICS_FW_UPGRADE) += touch_synaptics_fw_upgrade.o obj-$(CONFIG_LGE_TOUCH_SYNAPTICS) += DS4/ +obj-$(CONFIG_TOUCHSCREEN_APS_MELFAS) += aps_ts.o +obj-$(CONFIG_TOUCHSCREEN_ADS_AGS04) += ags04_ts.o diff --git a/drivers/input/touchscreen/ags04_ts.c b/drivers/input/touchscreen/ags04_ts.c new file mode 100644 index 000000000000..2f11942c0321 --- /dev/null +++ b/drivers/input/touchscreen/ags04_ts.c @@ -0,0 +1,592 @@ +/* + * Touch Sensor driver for AD Semiconductor AGS04 + * + * Copyright (C) 2013 LG Electronics Inc. + * Author: LG Electronics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#endif +#ifdef CONFIG_FB +#include +#include +#endif + +#define MAX_CHANNEL 4 + +#define REG_OUTPUT 0x00 +#define REG_CH_ENABLE 0x01 +#define REG_GLOBAL_CTRL1 0x02 +#define REG_INTERRUPT_MODE 0x03 +#define REG_SENSITIVITY1 0x04 +#define REG_SENSITIVITY2 0x05 +#define REG_SEN_LIMIT 0x08 +#define REG_CAL_SPEED 0x0C +#define REG_RND_CAL_SPEED 0x0D +#define REG_OUT_EXPIRATION 0x0E +#define REG_PERIOD_CONTROL 0x16 + +struct ags04_ts_platform_data { + int gpio_int; + int key_code[MAX_CHANNEL]; + int num_of_on_cmds; + int *on_cmds; +}; + +struct ags04_ts_info { + struct i2c_client *client; + struct input_dev *input_dev; + + char phys[32]; + + bool initialized; + bool enabled; + int output; + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif + struct mutex lock; + + struct ags04_ts_platform_data *pdata; + + struct regulator *vdd; + struct regulator *vcc_i2c; +}; + + +static int ags04_ts_i2c_read(struct i2c_client *client, int reg) +{ + int rc; + + rc = i2c_smbus_read_byte_data(client, reg); + if (rc < 0) { + dev_err(&client->dev, "i2c error %d while reading register %d\n", rc, reg); + } + + return rc; +} + +static ssize_t ags04_test_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int a=0, b=0, c=0, d=0, e=0, f=0, g=0, h=0; + + struct i2c_client *client = to_i2c_client(dev); + + a = ags04_ts_i2c_read(client, 0x02); + if (a < 0) + printk("[TK] 0x02 i2c read fail a\n"); + + b = ags04_ts_i2c_read(client, 0x0c); + if (b < 0) + printk("[TK] 0x0c i2c read fail a\n"); + + c = ags04_ts_i2c_read(client, 0x0d); + if (c < 0) + printk("[TK] 0x0d i2c read fail a\n"); + + d = ags04_ts_i2c_read(client, 0x0e); + if (d < 0) + printk("[TK] 0x0e i2c read fail a\n"); + + e = ags04_ts_i2c_read(client, 0x16); + if (e < 0) + printk("[TK] 0x16 i2c read fail a\n"); + + f = ags04_ts_i2c_read(client, 0x01); + if (f < 0) + printk("[TK] 0x01 i2c read fail a\n"); + + g = ags04_ts_i2c_read(client, 0x04); + if (g < 0) + printk("[TK] 0x04 i2c read fail a\n"); + + h = ags04_ts_i2c_read(client, 0x05); + if (h < 0) + printk("[TK] 0x05 i2c read fail a\n"); + + return sprintf(buf, "0x02[%x] 0x0c[%x] 0x0d[%x] 0x0e[%x] 0x16[%x] 0x01[%x] 0x04[%x] 0x05[%x]\n", a, b, c, d, e, f, g, h); +} + +static struct device_attribute dev_attr_device_test[] = { + __ATTR(test, S_IRUGO | S_IWUSR | S_IXOTH, ags04_test_show, NULL), +}; + +static int ags04_ts_i2c_write(struct i2c_client *client, int reg, int data) +{ + int rc; + + rc = i2c_smbus_write_byte_data(client, reg, data); + if (rc) { + dev_err(&client->dev, "i2c error %d while writing %d to register %d\n", rc, data, reg); + } + + return rc; +} + +static int ags04_ts_power_on(struct ags04_ts_info *info, int on) +{ + static bool initialized = false; + int rc = 0; + int retval = 0; + + struct i2c_client *client = info->client; + dev_info(&client->dev, "ags04_ts_power_on! initialized:%d\n", initialized); + /* TODO : Request power source */ + if (!initialized) { + if(info->vdd == NULL) { + info->vdd = regulator_get(&info->client->dev, "vdd"); + if (IS_ERR(info->vdd)) { + dev_err(&info->client->dev, "[TOUCH] %s: Failed to get vdd regulator\n", __func__); + return PTR_ERR(info->vdd); + } + dev_err(&info->client->dev, "[TOUCH] regulator_get(VDD) \n"); + } + #if 0 + if(info->vcc_i2c == NULL) { + info->vcc_i2c = regulator_get(&info->client->dev, "vcc_i2c"); + if (IS_ERR(info->vcc_i2c)) { + dev_err(&info->client->dev, "[TOUCH] %s: Failed to get i2c regulator\n", __func__); + return PTR_ERR(info->vcc_i2c); + } + dev_err(&info->client->dev, "[TOUCH] regulator_get(I2C) \n"); + } + #endif + + if(info->vdd) { + retval = regulator_set_voltage(info->vdd, 1800000, 1800000); + if (retval) + dev_err(&info->client->dev, "[TOUCH] regulator_set_voltage(VDD) failed retval=%d\n", retval); + else + dev_err(&info->client->dev, "[TOUCH] regulator_set_voltage(VDD) \n"); + } + #if 0 + if(info->vcc_i2c) { + retval = regulator_set_voltage(info->vcc_i2c, 1800000, 1800000); + if (retval) + dev_err(&info->client->dev, "[TOUCH] regulator_set_voltage(I2C) failed retval=%d\n", retval); + else + dev_err(&info->client->dev, "[TOUCH] regulator_set_voltage(I2C) \n"); + } + #endif + } + + /* TODO : Turn on/off power */ + if (initialized == false) + { + if (on) { + if(info->vdd) { + retval = regulator_enable(info->vdd); + dev_err(&info->client->dev, "[TOUCH] regulator_enable(VDD) \n"); + if (retval < 0) { + dev_err(&info->client->dev, "[TOUCH] Regulator vdd enable failed retval = %d\n", retval); + } + } + #if 0 + if(info->vcc_i2c) { + retval = regulator_enable(info->vcc_i2c); + dev_err(&info->client->dev, "[TOUCH] regulator_enable(I2C) \n"); + if (retval < 0) { + dev_err(&info->client->dev, "[TOUCH] Regulator i2c enable failed retval = %d\n", retval); + } + } + #endif + } else { + #if 0 + if(info->vdd) { + regulator_disable(info->vdd); + dev_err(&info->client->dev, "[TOUCH] regulator_disable(VDD) \n"); + } + + if(info->vcc_i2c) { + regulator_disable(info->vcc_i2c); + dev_err(&info->client->dev, "[TOUCH] regulator_disable(I2C) \n"); + } + #endif + } + } + initialized = true; + + return rc; +} + +static int ags04_ts_config(struct ags04_ts_info *info) +{ + struct i2c_client *client = info->client; + int rc = 0; + int i; + + if (!info->pdata->num_of_on_cmds) + return 0; + if (!info->pdata->on_cmds) + return 0; + + for(i = 0 ; i < info->pdata->num_of_on_cmds; i++) { + rc = ags04_ts_i2c_write(client, + info->pdata->on_cmds[(i * 2)], + info->pdata->on_cmds[(i * 2) + 1]); + } + + return rc; +} + +static void ags04_ts_enable(struct ags04_ts_info *info) +{ + struct i2c_client *client = info->client; + + if (info->enabled) + return; + + mutex_lock(&info->lock); + + //ags04_ts_power_on(info, 1); + //msleep(100); + + info->enabled = true; + enable_irq(client->irq); + + mutex_unlock(&info->lock); +} + +static void ags04_ts_disable(struct ags04_ts_info *info) +{ + struct i2c_client *client = info->client; + + if (!info->enabled) + return; + + mutex_lock(&info->lock); + + disable_irq(client->irq); + info->enabled = false; + + //ags04_ts_power_on(info, 0); + + mutex_unlock(&info->lock); +} + +static irqreturn_t ags04_ts_interrupt(int irq, void *dev_id) +{ + struct ags04_ts_info *info = dev_id; + struct i2c_client *client = info->client; + + int channel; + int changed; + int output; + int pressed; + + dev_info(&client->dev, "interrupt!\n"); + output = ags04_ts_i2c_read(client, REG_OUTPUT); + if (output < 0) + return IRQ_HANDLED; + + changed = info->output ^ output; + + for (channel = 0; channel < MAX_CHANNEL; channel++) { + if (changed & (1 << channel)) { + changed = changed ^ (1 << channel); + pressed = output >> channel & 0x01; + + if (info->pdata->key_code[channel]) { + input_report_key(info->input_dev, info->pdata->key_code[channel], pressed); + } else { + dev_err(&client->dev, "key code not defined at channel %d\n", channel); + } + } + } + input_sync(info->input_dev); + + info->output = output; + + return IRQ_HANDLED; +} + +static int ags04_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ags04_ts_info *info = i2c_get_clientdata(client); + + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) { + dev_info(&client->dev, "ags04_ts_suspend!\n"); + ags04_ts_disable(info); + } + + mutex_unlock(&info->input_dev->mutex); + return 0; +} + +static int ags04_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ags04_ts_info *info = i2c_get_clientdata(client); + + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) { + dev_info(&client->dev, "ags04_ts_resume!\n"); + ags04_ts_enable(info); + } + + mutex_unlock(&info->input_dev->mutex); + + return 0; +} + +static int ags04_ts_input_open(struct input_dev *dev) +{ + struct ags04_ts_info *info = input_get_drvdata(dev); + struct i2c_client *client = info->client; + dev_info(&client->dev, "ags04_ts_input_open!\n"); + //ags04_ts_enable(info); + info = info; + return 0; +} + +static void ags04_ts_input_close(struct input_dev *dev) +{ + struct ags04_ts_info *info = input_get_drvdata(dev); + struct i2c_client *client = info->client; + dev_info(&client->dev, "ags04_ts_input_close!\n"); + //ags04_ts_disable(info); + info = info; +} + + +#ifdef CONFIG_FB +static int ags04_ts_fb_notifier_call(struct notifier_block *self, + unsigned long event, + void *data) +{ + struct ags04_ts_info *info = container_of(self, struct ags04_ts_info, fb_notifier); + struct fb_event *evdata = data; + int *fb; + if(evdata && evdata->data && event == FB_EVENT_BLANK && info && info->client) { + fb = evdata->data; + switch (*fb) { + case FB_BLANK_UNBLANK: + ags04_ts_resume(&info->client->dev); + break; + case FB_BLANK_POWERDOWN: + ags04_ts_suspend(&info->client->dev); + break; + default: + break; + } + } + return 0; +} +#endif + +#ifdef CONFIG_OF +static int ags04_ts_parse_dt(struct device *dev, struct ags04_ts_platform_data *data) +{ + struct device_node *np = dev->of_node; + int rc; + + rc = of_property_read_u32(np, "ads,gpio-int", &data->gpio_int); + if (rc) { + dev_err(dev, "fail to read gpio-int (%d)\n", rc); + } + + rc = of_property_read_u32_array(np, "ads,key_code", data->key_code, MAX_CHANNEL); + if (rc) { + dev_err(dev, "fail to read key_code (%d)\n", rc); + } + + rc = of_property_read_u32(np, "ads,num-of-on-cmds", &data->num_of_on_cmds); + if (rc) { + dev_err(dev, "fail to read num-of-on-cmds (%d)\n", rc); + data->num_of_on_cmds = 0; + return 0; + } + + data->on_cmds = kzalloc(sizeof(int) * data->num_of_on_cmds * 2, GFP_KERNEL); + if (!data->on_cmds) { + dev_err(dev, "fail to read alloc memory for on-cmds\n"); + return -ENOMEM; + } + + rc = of_property_read_u32_array(np, "ads,on-cmds", data->on_cmds, data->num_of_on_cmds * 2); + if (rc) { + dev_err(dev, "fail to read on-cmds (%d)\n", rc); + kfree(data->on_cmds); + data->on_cmds = NULL; + } + + return 0; +} +#endif + +static int __devinit ags04_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct ags04_ts_info *info; + struct input_dev *input_dev; + int rc = 0; + int i = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!info || !input_dev) { + dev_err(&client->dev, "Failed to allocated memory\n"); + return -ENOMEM; + } + + info->client = client; + info->input_dev = input_dev; + info->output = 0; +#ifdef CONFIG_OF + info->pdata = kzalloc(sizeof(*info->pdata), GFP_KERNEL); + if (!info->pdata) { + dev_err(&client->dev, "Failed to allocated memory for device tree\n"); + return -ENOMEM; + } + rc = ags04_ts_parse_dt(&client->dev, info->pdata); + if (rc) { + dev_err(&client->dev, "Failed to parse device tree\n"); + return -ENODEV; + } +#else + info->pdata = client->dev.platform_data; +#endif + + mutex_init(&info->lock); + + snprintf(info->phys, sizeof(info->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = "ags04_ts"; + input_dev->phys = info->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = ags04_ts_input_open; + input_dev->close = ags04_ts_input_close; + + input_set_drvdata(input_dev, info); + + if (info->pdata->key_code[0]) { + int i = 0; + __set_bit(EV_KEY, input_dev->evbit); + while(info->pdata->key_code[i]) { + __set_bit(info->pdata->key_code[i], input_dev->keybit); + i++; + } + } + + rc = input_register_device(input_dev); + if (rc) { + dev_err(&client->dev, "failed to register input dev %d\n", rc); + return -EIO; + } + + i2c_set_clientdata(client, info); + +#ifdef CONFIG_FB + info->fb_notifier.notifier_call = ags04_ts_fb_notifier_call; + rc = fb_register_client(&info->fb_notifier); + if (rc) { + dev_err(&client->dev, "failed to register fb_notifier %d\n", rc); + } +#endif + + for (i = 0; i < ARRAY_SIZE(dev_attr_device_test); i++) { + rc = device_create_file(&client->dev, &dev_attr_device_test[i]); + if (rc) { + dev_err(&client->dev,"device_create_file fail %d\n", rc); + } + } + ags04_ts_power_on(info, 1); + msleep(200); + + rc = ags04_ts_config(info); + if (rc) { + dev_err(&client->dev, "failed to initialize IC %d\n", rc); + } + + if (gpio_is_valid(info->pdata->gpio_int)) { + gpio_request(info->pdata->gpio_int, "ags04_ts_int"); + gpio_direction_input(info->pdata->gpio_int); + } + + rc = request_threaded_irq(client->irq, NULL, ags04_ts_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ags04_ts", info); + if (rc) { + dev_err(&client->dev, "failed to register irq %d\n", rc); + return -ENODEV; + } + disable_irq(client->irq); + + return 0; +} + +static int __devexit ags04_ts_remove(struct i2c_client *client) +{ + struct ags04_ts_info *info = i2c_get_clientdata(client); + + info = info; + + return 0; +} + +static const struct i2c_device_id ags04_ts_id[] = { + {"ags04_ts", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, ags04_ts_id); + +#ifdef CONFIG_OF +static struct of_device_id ags04_ts_match_table[] = { + { .compatible = "ads,ags04_ts",}, + {}, +}; +#endif + +static struct i2c_driver ags04_ts_driver = { + .probe = ags04_ts_probe, + .remove = __devexit_p(ags04_ts_remove), + .driver = { + .name = "ags04_ts", +#ifdef CONFIG_OF + .of_match_table = ags04_ts_match_table, +#endif + }, + .id_table = ags04_ts_id, +}; + +static int __init ags04_ts_init(void) +{ + return i2c_add_driver(&ags04_ts_driver); +} + +static void __exit ags04_ts_exit(void) +{ + return i2c_del_driver(&ags04_ts_driver); +} + +module_init(ags04_ts_init); +module_exit(ags04_ts_exit); diff --git a/drivers/input/touchscreen/aps_ts.c b/drivers/input/touchscreen/aps_ts.c new file mode 100644 index 000000000000..12a8f2dff165 --- /dev/null +++ b/drivers/input/touchscreen/aps_ts.c @@ -0,0 +1,1898 @@ +#define DEBUG /* if DEBUG is activated dev_info will be printed */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_FB +#include +#include +#endif + +/* Matrix maximum */ +#define MAX_ROW 30 +#define MAX_COL 20 + + + +/* Show the touch log */ +#define KERNEL_LOG_MSG 1 + +#define MAX_FINGER_NUM 10 +#define FINGER_EVENT_SZ 8 +#define MAX_WIDTH 30 +#define MAX_PRESSURE 255 +#define MAX_LOG_LENGTH 128 + +/* Registers */ + +#define APS_REGH_CMD 0x10 + +#define APS_REGL_MODE_CONTROL 0x01 +#define APS_REGL_ROW_NUM 0x0B +#define APS_REGL_COL_NUM 0x0C +#define APS_REGL_RAW_TRACK 0x0E +#define APS_REGL_EVENT_PKT_SZ 0x0F +#define APS_REGL_INPUT_EVENT 0x10 +#define APS_REGL_UCMD 0xA0 +#define APS_REGL_UCMD_RESULT_LENGTH 0xAE +#define APS_REGL_UCMD_RESULT 0xAF + + +/* Event types */ +#define APS_ET_LOG 0xD +#define APS_ET_NOTIFY 0xE +#define APS_ET_ERROR 0xF + +/* ISP mode */ +#define ISP_ENTRY1 { 0xAC, 0xCE, 0xFF, 0xFD, 0xAC, 0xCE } +#define ISP_ENTRY2 { 0xAC, 0xCE, 0xFF, 0xFE, 0xAC, 0xCE } +#define ISP_ENTRY3 { 0xAC, 0xCE, 0xFF, 0xFF, 0xAC, 0xCE } + +#define ISP_UNLOCK1 { 0x00, 0x04, 0xFE, 0xDC, 0xBA, 0x98 } +#define ISP_UNLOCK2 { 0x00, 0x04, 0x76, 0x54, 0x32, 0x10 } +#define ISP_UNLOCK3 { 0x00, 0x4C, 0x05, 0xFA, 0x00, 0x05 } +#define ISP_LOCK { 0x00, 0x04, 0x05, 0xFA, 0x00, 0x00 } + +#define ISP_FULL_ERASE { 0x00, 0x10, 0x00, 0x00, 0x10, 0x44 } +#define ISP_CLEAR_DONE { 0x00, 0x0C, 0x00, 0x00, 0x00, 0x20 } +#define ISP_WIRTE_MODE { 0x00, 0x10, 0x00, 0x00, 0x10, 0x01 } +#define ISP_WIRTE { 0x00, 0x10, 0x00, 0x00, 0x10, 0x41 } +#define ISP_READ_MOED { 0x00, 0x10, 0x00, 0x00, 0x10, 0x08 } +#define ISP_READ { 0x00, 0x10, 0x00, 0x00, 0x10, 0x48 } + +/* Firmware file name */ +#define FW_NAME "aps_ts.fw" + + +enum { + GET_COL_NUM = 1, + GET_ROW_NUM, + GET_EVENT_DATA, +}; + +enum { + LOG_TYPE_U08 = 2, + LOG_TYPE_S08, + LOG_TYPE_U16, + LOG_TYPE_S16, + LOG_TYPE_U32 = 8, + LOG_TYPE_S32, +}; + +enum{ + SYS_INTENSITY = 2, + SYS_ROWNUM, + SYS_COLNUM, + SYS_CLEAR, + SYS_ENABLE, + SYS_DISABLE, +}; + +struct aps_ts_info { + struct i2c_client *client; + struct input_dev *input_dev; + char phys[32]; + + u8 row_num; + u8 col_num; + + int irq; + + struct aps_ts_platform_data *pdata; + char *fw_name; + struct completion init_done; +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif + struct mutex lock; + bool enabled; + + struct cdev cdev; + dev_t aps_dev; + struct class *class; + + char *cm_intensity; + u8 *raw_data; + + char raw_cmd; + int data_cmd; + struct aps_log_data { + u8 *data; + int cmd; + } log; +}; + + +static u16 raw[MAX_ROW][MAX_COL]; +static s16 cm[MAX_ROW][MAX_COL]; +static void aps_report_input_data(struct aps_ts_info *info, u8 sz, u8 *buf); + +static void aps_report_input_data(struct aps_ts_info *info, u8 sz, u8 *buf); +#ifdef CONFIG_FB +static int aps_ts_fb_notifier_call(struct notifier_block *self, unsigned long event, void *data); +#endif +static int aps_ts_config(struct aps_ts_info *info); + +static void aps_ts_enable(struct aps_ts_info *info) +{ + dev_dbg(&info->client->dev, "%s\n", __func__); + if (info->enabled) + return; + + mutex_lock(&info->lock); + + info->enabled = true; + enable_irq(info->irq); + + mutex_unlock(&info->lock); + +} + +static void aps_ts_disable(struct aps_ts_info *info) +{ + if (!info->enabled) + return; + dev_dbg(&info->client->dev, "%s\n", __func__); + + mutex_lock(&info->lock); + + disable_irq(info->irq); + + info->enabled = false; + + mutex_unlock(&info->lock); +} + +#if 0 +static void aps_reset() +{ + gpio_set_value(178, 1); + msleep(500); + gpio_set_value(178, 0); + msleep(500); + gpio_set_value(178, 1); + msleep(500); +} +#endif +static void aps_clear_input_data(struct aps_ts_info *info) +{ + int i; + dev_dbg(&info->client->dev, "%s\n", __func__); + + for (i = 0; i < MAX_FINGER_NUM; i++) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, false); + } + input_sync(info->input_dev); + + return; +} + +static int get_intensity(struct aps_ts_info *info) +{ + struct i2c_client *client = info->client; + + int col, row; + + int nIdx; + + u8 write_buf[8]; + u8 read_buf[MAX_ROW*2]; + + + u8 nLength = 0; + + s16 nIntensity; + + + for(col = 0 ; col < info->col_num ; col++) + { + + +#if KERNEL_LOG_MSG + printk("[%2d] ",col); +#endif + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD; + write_buf[2] = 0x70; + write_buf[3] = 0xFF; + write_buf[4] = col; + + if(i2c_master_send(client,write_buf,5)!=5) + { + dev_err(&client->dev, "intensity i2c send failed\n"); + enable_irq(info->irq); + return -1; + } + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT_LENGTH; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,1)!=1) + { + dev_err(&client->dev,"recv : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + nLength = read_buf[0]; + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,nLength)!=nLength) + { + dev_err(&client->dev,"recv : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + nLength >>=1; + for(row = 0 ; row col_num ; col++){ + for(row = 0 ; row < info->row_num ; row++){ + info->cm_intensity[nIdx++]=(char)((cm[col][row] &0xFF00)>>8); + info->cm_intensity[nIdx++]=(char)( cm[col][row] &0xFF); + } + } + + return 0; +} +static int get_rawdata(struct aps_ts_info *info) +{ + struct i2c_client *client = info->client; + int col, row; + int nIdx; +// u8 RefLevel; + u8 write_buf[8]; + u8 read_buf[MAX_ROW * 8]; + u8 nLength = 0; + //u16* p; + u16 nReference; +/* + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_REF_TRACK; + + + if(i2c_master_send(client, write_buf,2)!=2) + { + dev_err(&client->dev, "Ref track level i2c send failed\n"); + return -1; + } + if(i2c_master_recv(client, read_buf,1)!=1) + { + dev_err(&client->dev, "Ref track level i2c recv failed\n"); + return -1; + } + + RefLevel = read_buf[0];*/ + + disable_irq(info->irq); + + nIdx = 0; + for(col = 0 ; col < info->col_num ; col++) + { + printk("[%2d] ",col); + + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD; + write_buf[2] = 0x75; + write_buf[3] = 0xFF; + write_buf[4] = col; + + if(i2c_master_send(client,write_buf,5)!=5) + { + dev_err(&client->dev, "intensity i2c send failed\n"); + enable_irq(info->irq); + return -1; + } + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT_LENGTH; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,1)!=1) + { + dev_err(&client->dev,"recv1: i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + nLength = read_buf[0]; + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,nLength)!=nLength) + { + dev_err(&client->dev,"recv2: i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + nLength >>=1; + for(row = 0 ; row col_num ; col++){ + for(row = 0 ; row < info->row_num ; row++){ + info->raw_data[nIdx++]=(char)((raw[col][row] &0xFF00)>>8); + info->raw_data[nIdx++]=(char)( raw[col][row] &0xFF); + } + } + + enable_irq(info->irq); + return 0; +} +static int get_dtxdata(struct aps_ts_info *info) +{ + struct i2c_client *client = info->client; + + int col, row; + + int nIdx; + //u16* p; + u8 write_buf[8]; + u8 read_buf[MAX_ROW*2]; + + + u8 nLength = 0; + + s16 nIntensity; + + disable_irq(info->irq); + for(col = 0 ; col < info->col_num ; col++) + { + + +#if KERNEL_LOG_MSG + printk("[%2d] ",col); +#endif + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD; + write_buf[2] = 0x74; + write_buf[3] = 0xFF; + write_buf[4] = col; + + if(i2c_master_send(client,write_buf,5)!=5) + { + dev_err(&client->dev, "intensity i2c send failed\n"); + enable_irq(info->irq); + return -1; + } + + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT_LENGTH; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,1)!=1) + { + dev_err(&client->dev,"recv : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + nLength = read_buf[0]; + write_buf[0] = APS_REGH_CMD; + write_buf[1] = APS_REGL_UCMD_RESULT; + + if(i2c_master_send(client,write_buf,2)!=2) + { + dev_err(&client->dev,"send : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + if(i2c_master_recv(client,read_buf,nLength)!=nLength) + { + dev_err(&client->dev,"recv : i2c failed\n"); + enable_irq(info->irq); + return -1; + } + + + nLength >>=1; + for(row = 0 ; row irq); + nIdx = 0; + for(col = 0 ; col < info->col_num ; col++){ + for(row = 0 ; row < info->row_num ; row++){ + info->raw_data[nIdx++]=(char)((raw[col][row] &0xFF00)>>8); + info->raw_data[nIdx++]=(char)( raw[col][row] &0xFF); + } + } + + return 0; +} +static int aps_fs_open(struct inode *node, struct file *fp) +{ + struct aps_ts_info *info; + struct i2c_client *client; + struct i2c_msg msg; + u8 buf[4] = { + APS_REGH_CMD, + APS_REGL_UCMD, + 0x20, + true, + }; + + info = container_of(node->i_cdev, struct aps_ts_info, cdev); + client = info->client; + dev_dbg(&info->client->dev, "%s\n", __func__); + + disable_irq(info->irq); + fp->private_data = info; + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + i2c_transfer(client->adapter, &msg, 1); + + info->log.data = kzalloc(MAX_LOG_LENGTH * 20 + 5, GFP_KERNEL); + + aps_clear_input_data(info); + + return 0; +} + +static int aps_fs_release(struct inode *node, struct file *fp) +{ + struct aps_ts_info *info = fp->private_data; + struct i2c_client *client = info->client; + struct i2c_msg msg; + u8 buf[4] = { + APS_REGH_CMD, + APS_REGL_UCMD, + 0x20, + false, + }; + msg.addr = client->addr; + msg.flags = 0; + msg.buf = buf; + msg.len = sizeof(buf); + + + dev_dbg(&info->client->dev, "%s\n", __func__); + aps_clear_input_data(info); + + + i2c_transfer(client->adapter, &msg, 1); + kfree(info->log.data); + enable_irq(info->irq); + + return 0; +} + +static void aps_event_handler(struct aps_ts_info *info) +{ + struct i2c_client *client = info->client; + u8 sz; + int ret; + int row_num; + u8 reg[2] ={APS_REGH_CMD, APS_REGL_EVENT_PKT_SZ}; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = reg, + .len = 2, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = info->log.data, + }, + + }; + struct aps_log_pkt { + u8 marker; + u8 log_info; + u8 code; + u8 element_sz; + u8 row_sz; + u8 rigth_shift; + } __attribute__ ((packed)) *pkt = (struct aps_log_pkt *)info->log.data; + + dev_dbg(&info->client->dev, "%s\n", __func__); + memset(pkt, 0, sizeof(*pkt)); + + if (gpio_get_value(info->pdata->gpio_irq)) + return; + + if(i2c_master_send(client,reg,2)!=2) + dev_err(&client->dev,"send : i2c failed\n"); + if(i2c_master_recv(client,&sz,1)!=1) + dev_err(&client->dev,"recv : i2c failed\n"); + msg[1].len = sz; + + reg[1] = APS_REGL_INPUT_EVENT; + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(&client->dev, + "failed to read %d bytes of data\n", + sz); + return; + } + + if ((pkt->marker & 0xf) == APS_ET_LOG) { + if ((pkt->log_info & 0x7) == 0x1) { + pkt->element_sz = 0; + pkt->row_sz = 0; + + return; + } + + switch (pkt->log_info >> 4) { + case LOG_TYPE_U08: + case LOG_TYPE_S08: + msg[1].len = pkt->element_sz; + break; + case LOG_TYPE_U16: + case LOG_TYPE_S16: + msg[1].len = pkt->element_sz * 2; + break; + case LOG_TYPE_U32: + case LOG_TYPE_S32: + msg[1].len = pkt->element_sz * 4; + break; + default: + dev_err(&client->dev, "invalied log type\n"); + return; + } + + msg[1].buf = info->log.data + sizeof(struct aps_log_pkt); + reg[1] = APS_REGL_UCMD_RESULT; + row_num = pkt->row_sz ? pkt->row_sz : 1; + + while (row_num--) { + while (gpio_get_value(info->pdata->gpio_irq)) + ; + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + msg[1].buf += msg[1].len; + }; + } else { + aps_report_input_data(info, sz, info->log.data); + memset(pkt, 0, sizeof(*pkt)); + } + + return; +} + + +static void aps_report_input_data(struct aps_ts_info *info, u8 sz, u8 *buf) +{ + int i; + struct i2c_client *client = info->client; + int id; + int x; + int y; + int touch_major; + int pressure; + u8 *tmp; + + if (buf[0] == APS_ET_NOTIFY) { + dev_dbg(&client->dev, "TSP mode changed (%d)\n", buf[1]); + goto out; + } else if (buf[0] == APS_ET_ERROR) { + dev_info(&client->dev, "Error detected, restarting TSP\n"); + aps_clear_input_data(info); + goto out; + } + + for (i = 0; i < sz; i += FINGER_EVENT_SZ) { + tmp = buf + i; + + id = (tmp[0] & 0xf) -1; + x = tmp[2] | ((tmp[1] & 0xf) << 8); + y = tmp[3] | (((tmp[1] >> 4 ) & 0xf) << 8); + touch_major = tmp[4]; + pressure = tmp[5]; + if (pressure == 0) { + dev_info(&client->dev, "id:%d, x:%d, y:%d, tourch_major:%d, pressure:%d\n", id, x,y,touch_major,pressure); + } + input_mt_slot(info->input_dev, id); + + if (!(tmp[0] & 0x80)) { + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, false); + continue; + } + //printk("[TOUCH]%s finger = %d, x = %d, y = %d, width = %d, pressure = %d \n", __func__, id, x, y, touch_major, pressure); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, true); + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(info->input_dev, ABS_MT_PRESSURE, pressure); + + } + + input_sync(info->input_dev); + +out: + return; + +} + + + +static ssize_t aps_fs_read(struct file *fp, char *rbuf, size_t cnt, loff_t *fpos) +{ + struct aps_ts_info *info = fp->private_data; + struct i2c_client *client = info->client; + int ret = 0; + dev_dbg(&info->client->dev, "%s\n", __func__); + + switch (info->log.cmd) { + case GET_COL_NUM: + ret = copy_to_user(rbuf, &info->col_num, 1); + break; + case GET_ROW_NUM: + ret = copy_to_user(rbuf, &info->row_num, 1); + break; + case GET_EVENT_DATA: + aps_event_handler(info); + /* copy data without log marker */ + ret = copy_to_user(rbuf, info->log.data + 1, cnt); + break; + default: + dev_err(&client->dev, "unknown command\n"); + ret = -EFAULT; + break; + } + + return ret; +} + +static ssize_t aps_fs_write(struct file *fp, const char *wbuf, size_t cnt, loff_t *fpos) +{ + struct aps_ts_info *info = fp->private_data; + struct i2c_client *client = info->client; + u8 *buf; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = cnt, + }; + int ret = 0; + dev_dbg(&info->client->dev, "%s\n", __func__); + + mutex_lock(&info->lock); + + if (!info->enabled) + goto tsp_disabled; + + msg.buf = buf = kzalloc(cnt + 1, GFP_KERNEL); + + if ((buf == NULL) || copy_from_user(buf, wbuf, cnt)) { + dev_err(&client->dev, "failed to read data from user\n"); + ret = -EIO; + goto out; + } + + if (cnt == 1) { + info->log.cmd = *buf; + } else { + if (i2c_transfer(client->adapter, &msg, 1) != 1) { + dev_err(&client->dev, "failed to transfer data\n"); + ret = -EIO; + goto out; + } + } + + ret = 0; + +out: + kfree(buf); +tsp_disabled: + mutex_unlock(&info->lock); + + return ret; +} + +static int aps_isp_entry(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + int cnt=0, ret = 0; + u8 entry_a[6] = ISP_ENTRY1; + u8 entry_b[6] = ISP_ENTRY2; + u8 entry_c[6] = ISP_ENTRY3; + + dev_dbg(&info->client->dev, "%s\n", __func__); + +retry_entry_a: + if(i2c_master_send(client, entry_a, 6) != 6){ + if (cnt <50){ + cnt++; + printk("%s, retry_entry_a, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_entry_a; + } + cnt=0; + ret = -1; + return ret; + } + usleep(500); + cnt=0; +retry_entry_b: + + if(i2c_master_send(client, entry_b, 6) != 6){ + if (cnt <50){ + cnt++; + printk("%s, retry_entry_b, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_entry_b; + } + cnt=0; + ret = -1; + return ret; + } + usleep(500); + cnt=0; +retry_entry_c: + if(i2c_master_send(client, entry_c, 6) != 6){ + if (cnt <50){ + cnt++; + printk("%s, retry_entry_c, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_entry_c; + } + cnt=0; + ret = -1; + return ret; + } + usleep(500); + return ret; +} + +static int aps_isp_unlock(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + int cnt=0, ret = 0; + u8 unlock_a[6] = ISP_UNLOCK1; + u8 unlock_b[6] = ISP_UNLOCK2; + u8 unlock_c[6] = ISP_UNLOCK3; + + dev_dbg(&info->client->dev, "%s\n", __func__); + +retry_unlock_c: + if(i2c_master_send(client, unlock_c, 6) != 6){ + if (cnt <100){ + cnt++; + printk("%s, retry_unlock_c, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_unlock_c; + } + cnt=0; + ret = -1; + return ret; + } + cnt=0; + usleep(500); +retry_unlock_a: + if(i2c_master_send(client, unlock_a, 6) != 6){ + if (cnt <3){ + cnt++; + printk("%s, retry_unlock_a, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_unlock_a; + } + cnt=0; + ret = -1; + return ret; + } + usleep(500); + cnt=0; + +retry_unlock_b: + if(i2c_master_send(client, unlock_b, 6) != 6){ + if (cnt <3){ + cnt++; + printk("%s, retry_unlock_b, cnt=%d\n",__func__, cnt); + usleep(500); + goto retry_unlock_b; + } + cnt=0; + ret = -1; + return ret; + } + usleep(500); + cnt=0; + + return ret; +} +static int aps_isp_write_done(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + u8 cmd[2] = {0x00, 0x0C}; + u8 rbuf[4]; + int ret = 0; + int i = 0; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + },{ + .addr = client->addr, + .flags = I2C_M_RD, + .buf = rbuf, + .len = 4, + }, + }; + + dev_dbg(&info->client->dev, "%s\n", __func__); + while(true){ + if(i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg))!=ARRAY_SIZE(msg)){ + dev_err(&client->dev,"isp write done i2c_transfer failed\n"); + ret = -1; + return ret; + } + if(rbuf[3] & 0x20){ + break; + } + dev_err(&client->dev," write done while\n"); + + if (i++ > 1000){ + dev_err(&client->dev,"isp erase wait-done failed\n"); + ret = -1; + return ret; + } + } + return ret; +} +static int aps_isp_clean(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + int ret = 0; + u8 clr_done[6] = ISP_CLEAR_DONE; + + dev_dbg(&info->client->dev, "%s\n", __func__); + if( i2c_master_send(client,clr_done, 6)!= 6){ + dev_err(&client->dev,"isp clr_done i2c_master_send failed\n"); + ret = -1; + return ret; + } + return ret; +} +static int aps_isp_erase(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + u8 erase[6] = ISP_FULL_ERASE; + int ret = 0; + + dev_dbg(&info->client->dev, "%s\n", __func__); + if(i2c_master_send(client, erase,6)!=6){ + dev_err(&client->dev,"isp erase i2c_transfer failed\n"); + ret = -1; + return ret; + } + + ret = aps_isp_write_done(info); + + ret = aps_isp_clean(info); + + return ret; +} + +static int aps_isp_lock(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + u8 buf[6] = ISP_LOCK; + int ret = 0; + dev_dbg(&info->client->dev, "%s\n", __func__); + if(i2c_master_send(client, buf, 6)!=6){ + ret = -1; + dev_err(&client->dev,"isp fw-addr i2c_master_send failed\n"); + return ret; + } + return ret; +} + +static int aps_isp_addr(struct aps_ts_info *info, const size_t addr){ + struct i2c_client *client = info->client; + int ret = 0; + u8 wbuf_addr[6]; + wbuf_addr[0] = 0x00; + wbuf_addr[1] = 0x14; + wbuf_addr[2] = (addr>>24)&0xFF; + wbuf_addr[3] = (addr>>16)&0xFF; + wbuf_addr[4] = (addr>>8)&0xFF; + wbuf_addr[5] = (addr>>0)&0xFF; + + dev_dbg(&info->client->dev, "%s\n", __func__); + if(i2c_master_send(client, wbuf_addr, 6)!=6){ + ret = -1; + dev_err(&client->dev,"isp fw-addr i2c_master_send failed\n"); + return ret; + } + + return ret; +} + +static int aps_flash_data(struct aps_ts_info *info, const u8 *data, const size_t addr){ + struct i2c_client *client = info->client; + int ret = 0; + u8 write_mode[6] = ISP_WIRTE_MODE; + u8 write_cmd[6] = ISP_WIRTE; + u8 wbuf_a[6] = {0x00, 0x28, }; + u8 wbuf_b[6] = {0x00, 0x2C, }; + + dev_dbg(&info->client->dev, "%s\n", __func__); + if(i2c_master_send(client, write_mode, 6)!=6){ + dev_err(&client->dev,"isp write mode i2c_master_send failed\n"); + ret = -1; + return ret; + } + + wbuf_a[2] = data[3]; + wbuf_a[3] = data[2]; + wbuf_a[4] = data[1]; + wbuf_a[5] = data[0]; + + if(i2c_master_send(client, wbuf_a, 6)!=6){ + ret = -1; + dev_err(&client->dev,"isp fw-data(a) i2c_master_send failed\n"); + return ret; + } + + wbuf_b[2] = data[7]; + wbuf_b[3] = data[6]; + wbuf_b[4] = data[5]; + wbuf_b[5] = data[4]; + + if(i2c_master_send(client, wbuf_b, 6)!=6){ + ret = -1; + dev_err(&client->dev,"isp fw-data(b) i2c_master_send failed\n"); + return ret; + } + + ret = aps_isp_addr(info, addr); + + if(i2c_master_send(client, write_cmd, 6)!=6){ + dev_err(&client->dev,"isp write cmd i2c_master_send failed\n"); + ret = -1; + return ret; + } + + ret = aps_isp_write_done(info); + ret = aps_isp_clean(info); + return ret; +} +#if 0 +static int aps_read_data(struct aps_ts_info *info){ + struct i2c_client *client = info->client; + int ret = 0; + u8 read_a[2] = {0x00, 0x0C}; + //u8 read_b[2] = {0x00, 0x34}; + u8 rbuf[4]; + u8 r_cmp[8]; + //u8 read_mode[6] = ISP_READ_MOED; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = read_a, + .len =2, + },{ + .addr = client->addr, + .flags = I2C_M_RD, + .buf = rbuf, + .len = 4, + }, + }; + + if(i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg))!=ARRAY_SIZE(msg)){ + ret= -1; + dev_err(&client->dev,"isp read data(a) i2c_master_send failed\n"); + return ret; + } + r_cmp[0] = rbuf[3]; + r_cmp[1] = rbuf[2]; + r_cmp[2] = rbuf[1]; + r_cmp[3] = rbuf[0]; + + printk("%s, read data - 0x%x, 0x%x, 0x%x, 0x%x\n",__func__, rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + + return 0; + +} + +#endif + +static int aps_verify_data(struct aps_ts_info *info, const u8 *data, const size_t addr){ + struct i2c_client *client = info->client; + int ret = 0; + u8 read_a[2] = {0x00, 0x30}; + u8 read_b[2] = {0x00, 0x34}; + u8 rbuf[4]; + u8 r_cmp[8]; + u8 read_mode[6] = ISP_READ_MOED; + u8 read_cmd[6] = ISP_READ; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = read_a, + .len =2, + },{ + .addr = client->addr, + .flags = I2C_M_RD, + .buf = rbuf, + .len = 4, + }, + }; + dev_dbg(&info->client->dev, "%s\n", __func__); + if(i2c_master_send(client, read_mode, 6)!=6){ + ret= -1; + dev_err(&client->dev,"isp read mode i2c_master_send failed\n"); + return ret; + } + ret = aps_isp_addr(info, addr); + if(i2c_master_send(client, read_cmd, 6)!=6){ + ret= -1; + dev_err(&client->dev,"isp read cmd i2c_master_send failed\n"); + return ret; + } + if(i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg))!=ARRAY_SIZE(msg)){ + ret= -1; + dev_err(&client->dev,"isp read data(a) i2c_master_send failed\n"); + return ret; + } + r_cmp[0] = rbuf[3]; + r_cmp[1] = rbuf[2]; + r_cmp[2] = rbuf[1]; + r_cmp[3] = rbuf[0]; + + msg[0].buf = read_b; + + if(i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg))!=ARRAY_SIZE(msg)){ + ret= -1; + dev_err(&client->dev,"isp read data(b) i2c_master_send failed\n"); + return ret; + } + + + r_cmp[4] = rbuf[3]; + r_cmp[5] = rbuf[2]; + r_cmp[6] = rbuf[1]; + r_cmp[7] = rbuf[0]; + + ret = aps_isp_write_done(info); + + if(memcmp(data,r_cmp, 8)){ + ret =-1; + return ret; + } + + ret = aps_isp_clean(info); + return ret; +} + +static int aps_fw_flash(const struct firmware *fw, struct aps_ts_info *info){ + u8 *fw_data; + int addr, prgs; + int ret = 0; + int fw_size = (int)fw->size; + fw_data=kmalloc(0x10000, GFP_KERNEL); + disable_irq(info->irq); + memset(fw_data,0xff, 0x10000); + memcpy(fw_data,fw->data,fw->size); + if((fw_size % 8 )!=0) + fw_size = fw_size + ( 8 - ( fw_size % 8 )); + + if(aps_isp_entry(info)){ + dev_err(&info->client->dev,"isp entry failed\n"); + ret = -1; + goto out; + } + + if(aps_isp_unlock(info)){ + dev_err(&info->client->dev,"isp unlock failed\n"); + ret = -1; + goto out; + } + + if(aps_isp_erase(info)){ + dev_err(&info->client->dev,"isp erase failed\n"); + ret = -1; + goto out; + } + + for(addr = fw_size - 8 ; addr >= 0 ; addr -= 8){ + if(((fw_size - 8 - addr)%4096)==0){ + prgs=100 - ((100*addr)/(fw_size - 8)); + dev_info(&info->client->dev,"%s, Melfas Firmware flashing is on progress. %d%%. \n", __func__,prgs); + } + if(aps_flash_data(info,(u8 *)&fw_data[addr], addr )){ + dev_err(&info->client->dev,"isp flash failed\n"); + ret = -1; + goto out; + } + if(aps_verify_data(info,(u8 *)&fw_data[addr], addr )){ + dev_err(&info->client->dev,"isp verify failed\n"); + ret = -1; + goto out; + } + } + + ret = aps_isp_lock(info); + +out: + kfree(fw_data); + release_firmware(fw); + enable_irq(info->irq); + return ret; +} + +static void aps_fw_update_controller(const struct firmware *fw, void * context) +{ + struct aps_ts_info *info = context; + int ret; + + dev_info(&info->client->dev, "%s\n", __func__); + ret = aps_fw_flash(fw,info); + if(ret){ + dev_err(&info->client->dev, "firmware flash failed"); + }else{ + dev_dbg(&info->client->dev, "firmware flash succeed"); + } + gpio_direction_output(info->pdata->gpio_reset, 0); + msleep(10); + gpio_direction_output(info->pdata->gpio_reset, 1); + msleep(10); + dev_info(&info->client->dev, "%s, Melfas firmware flash DONE. 100%%.\n", __func__); + return; +} +static ssize_t aps_start_isp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct aps_ts_info *info = dev_get_drvdata(dev); + + const char *fw_name = FW_NAME; + int ret = 0; + + dev_dbg(&info->client->dev, "buf data = %s\n",fw_name); + info->fw_name = kstrdup(fw_name, GFP_KERNEL); + + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, &info->client->dev, + GFP_KERNEL, info, aps_fw_update_controller); + + if (ret) { + dev_err(&info->client->dev, "failed to schedule firmware update\n"); + return -EIO; + } + + kfree(info->fw_name); + return 1; +} + +static ssize_t aps_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + //struct aps_ts_info *info = dev_get_drvdata(dev); + + return count; +} + +static DEVICE_ATTR(aps_isp, 0666, aps_start_isp, aps_store); + +static struct attribute *aps_attrs[] = { + &dev_attr_aps_isp.attr, + NULL, +}; + +static const struct attribute_group aps_attr_group = { + .attrs = aps_attrs, +}; + + + +static irqreturn_t aps_ts_interrupt(int irq, void *dev_id) +{ + struct aps_ts_info *info = dev_id; + struct i2c_client *client = info->client; + u8 buf[MAX_FINGER_NUM * FINGER_EVENT_SZ] = { 0, }; + int ret; + u8 sz=0; + u8 reg[2] = {APS_REGH_CMD, APS_REGL_INPUT_EVENT}; + u8 cmd[2] = {APS_REGH_CMD, APS_REGL_EVENT_PKT_SZ}; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = &sz, + }, + }; + + msg[1].len = 1; + i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + msg[0].buf = reg; + msg[1].buf = buf; + msg[1].len = sz; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + if (ret != ARRAY_SIZE(msg)) { + dev_err(&client->dev, + "failed to read %d bytes of touch data (%d)\n", + sz, ret); + } else { + aps_report_input_data(info, sz, buf); + } + + return IRQ_HANDLED; +} + +static int aps_ts_input_open(struct input_dev *dev) +{ + struct aps_ts_info *info = input_get_drvdata(dev); + int ret; + + dev_dbg(&info->client->dev, "%s\n", __func__); + ret = wait_for_completion_interruptible_timeout(&info->init_done, + msecs_to_jiffies(90 * MSEC_PER_SEC)); + + if (ret > 0) { + if (info->irq != -1) { + aps_ts_enable(info); + ret = 0; + } else { + ret = -ENXIO; + } + } else { + dev_err(&dev->dev, "error while waiting for device to init\n"); + ret = -ENXIO; + } + + return ret; +} + +static void aps_ts_input_close(struct input_dev *dev) +{ + struct aps_ts_info *info = input_get_drvdata(dev); + + aps_ts_disable(info); +} + +static int aps_ts_config(struct aps_ts_info *info) +{ + struct i2c_client *client = info->client; + int ret; + u8 cmd[2] = {APS_REGH_CMD, APS_REGL_ROW_NUM}; + u8 num[2]; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + },{ + .addr = client->addr, + .flags = I2C_M_RD, + .buf = num, + .len = 2, + }, + }; + + dev_dbg(&info->client->dev, "%s\n", __func__); + //info->irq=gpio_to_irq(info->pdata->gpio_irq); + ret = request_threaded_irq(client->irq, NULL, aps_ts_interrupt, + (int)info->pdata->irq_flags, + LGD_TOUCH_NAME, info); + + if (ret) { + dev_err(&client->dev, "failed to register irq\n"); + goto out; + } + + disable_irq(client->irq); + info->irq = client->irq; + barrier(); + + ret=i2c_transfer(client->adapter,msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(&client->dev, + "failed to read bytes of touch data (%d)\n", + ret); + } + info->row_num = 20;//num[0]; + info->col_num = 12;//num[1]; + printk("row %d\n",info->row_num); + printk("col %d\n",info->col_num); + dev_dbg(&client->dev, "APS touch controller initialized\n"); + + complete_all(&info->init_done); + +out: + return ret; +} +static ssize_t bin_report_read(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + + u8 result; + result = 1; + count = 0; + + dev_dbg(&info->client->dev, "%s\n", __func__); + switch(info->data_cmd){ + case SYS_INTENSITY: + dev_info(&info->client->dev, "Intensity Test\n"); + if(get_intensity(info)!=0){ + dev_err(&info->client->dev, "Intensity Test failed\n"); + return -1; + } + count = (info->row_num * 2) * info->col_num; + dev_info(&info->client->dev, "%d",count); + memcpy(buf,info->cm_intensity,count); + info->data_cmd=0; + break; + case SYS_ROWNUM: + dev_info(&info->client->dev, "row send %d \n",info->row_num); + buf[0]=info->row_num; + count =1; + info->data_cmd=0; + break; + case SYS_COLNUM: + dev_info(&info->client->dev, "cloumn send%d\n", info->col_num); + buf[0]=info->col_num; + count =1; + info->data_cmd=0; + break; + case SYS_CLEAR: + dev_info(&info->client->dev, "Input clear\n"); + aps_clear_input_data(info); + info->data_cmd=0; + break; + case SYS_ENABLE: + dev_info(&info->client->dev, "enable_irq \n"); + enable_irq(info->irq); + info->data_cmd=0; + break; + case SYS_DISABLE: + dev_info(&info->client->dev, "disable_irq \n"); + disable_irq(info->irq); + info->data_cmd=0; + break; + } + return count; +} + +static ssize_t bin_report_write(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + dev_info(&info->client->dev, "%s\n", __func__); + + info->data_cmd=(int)buf[0]; + return count; + +} + +static struct bin_attribute bin_attr_data = { + .attr = { + .name = "report_data", + .mode = S_IRWXUGO, + }, + .size = PAGE_SIZE, + .read = bin_report_read, + .write = bin_report_write, +}; + +static ssize_t bin_ref_read(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + + switch(info->raw_cmd) + { + case '1': + + get_dtxdata(info); + count = (info->row_num * info->col_num) * sizeof(u16); + dev_dbg(&info->client->dev, "%d",count); + memcpy(buf,info->raw_data , count); + info->raw_cmd = '0'; + break; + case '2': + get_rawdata(info); + count = (info->row_num * info->col_num) * sizeof(u16); + dev_dbg(&info->client->dev, "%d",count); + memcpy(buf,info->raw_data , count); + info->raw_cmd = '0'; + break; + default: + break; + } + + return count; +} + +static ssize_t bin_ref_write(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + + info->raw_cmd=(char)buf[0]; + + return count; +} +static struct bin_attribute bin_attr_rawdata = { + .attr = { + .name = "raw_data", + .mode = S_IRWXUGO, + }, + .size = PAGE_SIZE, + .read = bin_ref_read, + .write = bin_ref_write, +}; + +static struct file_operations aps_fops = { + .owner = THIS_MODULE, + .open = aps_fs_open, + .release = aps_fs_release, + .read = aps_fs_read, + .write = aps_fs_write, +}; + +static int lgd_incell_parse_dt(struct device *dev, + struct aps_ts_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + u32 temp_val;//, num_buttons; + int rc; + + printk("[TOUCH]*******%s\n",__func__); + + + rc = of_property_read_u32(np, "lgd_melfas,max_x", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read max_x\n"); + return rc; + } else { + pdata->max_x = temp_val; + } + + rc = of_property_read_u32(np, "lgd_melfas,max_y", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read max_y\n"); + return rc; + } else { + pdata->max_y = temp_val; + } + +/* rc = of_property_read_u32(np, "lgd,id_min", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read id_min\n"); + return rc; + } else { + pdata->id_min = temp_val; + } + + rc = of_property_read_u32(np, "lgd,max_fingers", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read max_fingers\n"); + return rc; + } else { + pdata->max_fingers = temp_val; + } +*/ + /* irq gpio info */ + + pdata->gpio_irq = of_get_named_gpio_flags(np, + "lgd_melfas,irq_gpio", 0, &pdata->irq_flags); + + printk("[TOUCH]gpio_irq %d\n", pdata->gpio_irq); + printk("[TOUCH]irq_flags %d\n", (int)pdata->irq_flags); + + pdata->gpio_reset = of_get_named_gpio_flags(np, + "lgd_melfas,reset_gpio", 0, &pdata->reset_flags); + + printk("[TOUCH]gpio_reset %d\n", pdata->gpio_reset); + printk("[TOUCH]reset_flags %d\n", (int)pdata->reset_flags); + + pdata->name = LGD_TOUCH_NAME; + +#if 0 + prop = of_find_property(np, "synaptics,button-map", NULL); + if (prop) { + num_buttons = prop->length / sizeof(temp_val); + + rmi4_pdata->capacitance_button_map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->capacitance_button_map), + GFP_KERNEL); + if (!rmi4_pdata->capacitance_button_map) + return -ENOMEM; + + rmi4_pdata->capacitance_button_map->map = devm_kzalloc(dev, + sizeof(*rmi4_pdata->capacitance_button_map->map) * + MAX_NUMBER_OF_BUTTONS, GFP_KERNEL); + if (!rmi4_pdata->capacitance_button_map->map) + return -ENOMEM; + + if (num_buttons <= MAX_NUMBER_OF_BUTTONS) { + rc = of_property_read_u32_array(np, + "synaptics,button-map", button_map, + num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + for (i = 0; i < num_buttons; i++) + rmi4_pdata->capacitance_button_map->map[i] = + button_map[i]; + rmi4_pdata->capacitance_button_map->nbuttons = + num_buttons; + } else { + return -EINVAL; + } + } +#endif + return 0; +} + +static int __devinit aps_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct aps_ts_info *info; + struct input_dev *input_dev; + struct aps_ts_platform_data *platform_data = client->dev.platform_data; + + int ret = 0; + int result; + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return -EIO; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!info || !input_dev) { + dev_err(&client->dev, "Failed to allocated memory\n"); + return -ENOMEM; + } + + info->client = client; + info->input_dev = input_dev; + info->pdata = client->dev.platform_data; + init_completion(&info->init_done); + info->irq = -1; + + printk("[TOUCH]%s start\n",__func__); + + if (client->dev.of_node) { + platform_data = devm_kzalloc(&client->dev, + sizeof(*platform_data), + GFP_KERNEL); + if (!platform_data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + ret = lgd_incell_parse_dt(&client->dev, platform_data); + if (ret) + return ret; + } else { + info->pdata = client->dev.platform_data; + } + + info->pdata = platform_data; + + + + mutex_init(&info->lock); + + input_mt_init_slots(input_dev, MAX_FINGER_NUM); + + snprintf(info->phys, sizeof(info->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = "aps_ts"; + input_dev->phys = info->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = aps_ts_input_open; + input_dev->close = aps_ts_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_WIDTH, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, info->pdata->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, info->pdata->max_y, 0, 0); + + input_set_drvdata(input_dev, info); + + ret = input_register_device(input_dev); + if (ret) { + dev_err(&client->dev, "failed to register input dev\n"); + return -EIO; + } + + //Added reset pin high. + ret = gpio_request(info->pdata->gpio_reset, "touch_reset"); + if(ret < 0){ + printk(KERN_ERR "[TOUCH]can't request qpio!\n"); + } + gpio_direction_output(info->pdata->gpio_reset, 1); + msleep(10); + + printk("[TOUCH]%s i2c_set_clientdata\n",__func__); + i2c_set_clientdata(client, info); + + aps_ts_config(info); + +#ifdef CONFIG_FB + info->fb_notifier.notifier_call = aps_ts_fb_notifier_call; + ret = fb_register_client(&info->fb_notifier); + if (ret) { + dev_err(&client->dev, "%s: failed to register fb_notifier: %d\n", __func__, ret); + } +#endif + + if (alloc_chrdev_region(&info->aps_dev, 0, 1, "aps_ts")) { + dev_err(&client->dev, "failed to allocate device region\n"); + return -ENOMEM; + } + + cdev_init(&info->cdev, &aps_fops); + info->cdev.owner = THIS_MODULE; + + if (cdev_add(&info->cdev, info->aps_dev, 1)) { + dev_err(&client->dev, "failed to add ch dev\n"); + return -EIO; + } + + info->class = class_create(THIS_MODULE, "aps_ts"); + device_create(info->class, NULL, info->aps_dev, NULL, "aps_ts"); + result = sysfs_create_bin_file(&client->dev.kobj ,&bin_attr_data); + result = sysfs_create_bin_file(&client->dev.kobj ,&bin_attr_rawdata); + if (sysfs_create_link(NULL, &client->dev.kobj, "aps_ts")) { + dev_err(&client->dev, "failed to create sysfs symlink\n"); + return -EAGAIN; + } + + if (sysfs_create_group(&client->dev.kobj, &aps_attr_group)) { + dev_err(&client->dev, "failed to create sysfs group\n"); + return -EAGAIN; + } + dev_notice(&client->dev, "aps dev initialized\n"); + info->cm_intensity = kzalloc(2048 , GFP_KERNEL); + info->raw_data = kzalloc(MAX_COL*MAX_ROW * sizeof(u16) , GFP_KERNEL); + return 0; +} + +static int __devexit aps_ts_remove(struct i2c_client *client) +{ + struct aps_ts_info *info = i2c_get_clientdata(client); + + if (info->irq >= 0) + free_irq(info->irq, info); + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_rawdata); + sysfs_remove_bin_file(&client->dev.kobj, &bin_attr_data); + sysfs_remove_group(&info->client->dev.kobj, &aps_attr_group); + sysfs_remove_link(NULL, "aps_ts"); + input_unregister_device(info->input_dev); +#ifdef CONFIG_FB + fb_unregister_client(&info->fb_notifier); +#endif + + device_destroy(info->class, info->aps_dev); + class_destroy(info->class); + kfree(info->raw_data); + kfree(info->cm_intensity); + kfree(info->fw_name); + kfree(info); + + return 0; +} + +static int aps_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + + dev_info(&info->client->dev, "%s\n", __func__); + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) { + aps_ts_disable(info); + aps_clear_input_data(info); + } + + mutex_unlock(&info->input_dev->mutex); + gpio_direction_output(info->pdata->gpio_reset, 0); + return 0; + +} + +static int aps_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aps_ts_info *info = i2c_get_clientdata(client); + + dev_info(&info->client->dev, "%s\n", __func__); + gpio_direction_output(info->pdata->gpio_reset, 1); + //msleep(10); + mutex_lock(&info->input_dev->mutex); + + if (info->input_dev->users) + aps_ts_enable(info); + + mutex_unlock(&info->input_dev->mutex); + + return 0; +} +#ifdef CONFIG_FB +static int aps_ts_fb_notifier_call(struct notifier_block *self, + unsigned long event, + void *data) +{ + struct fb_event *evdata = data; + int *fb; + struct aps_ts_info *info = container_of(self, struct aps_ts_info, fb_notifier); + dev_info(&info->client->dev, "%s\n", __func__); + if(evdata && evdata->data && event == FB_EVENT_BLANK && info && info->client) { + fb = evdata->data; + switch (*fb) { + case FB_BLANK_UNBLANK: + aps_ts_resume(&info->client->dev); + break; + case FB_BLANK_POWERDOWN: + aps_ts_suspend(&info->client->dev); + break; + default: + break; + } + } + return 0; +} +#endif + +static const struct i2c_device_id aps_ts_id[] = { + {"aps_ts", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, aps_ts_id); + +static struct of_device_id lgd_match_table[] = { + { .compatible = "lgd_melfas,incell",}, + { },}; + +static struct i2c_driver aps_ts_driver = { + .probe = aps_ts_probe, + .remove = __devexit_p(aps_ts_remove), + .driver = { + .name = "aps_ts", + .of_match_table = lgd_match_table, + }, + .id_table = aps_ts_id, +}; + +static int __init aps_ts_init(void) +{ + return i2c_add_driver(&aps_ts_driver); +} + +static void __exit aps_ts_exit(void) +{ + return i2c_del_driver(&aps_ts_driver); +} + +module_init(aps_ts_init); +module_exit(aps_ts_exit); + +MODULE_VERSION("0.1"); +MODULE_DESCRIPTION("APS Touchscreen driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/input/aps_ts.h b/include/linux/input/aps_ts.h new file mode 100644 index 000000000000..b9cb6d8d1c74 --- /dev/null +++ b/include/linux/input/aps_ts.h @@ -0,0 +1,17 @@ + +#ifndef _LINUX_APS_TOUCH_H +#define _LINUX_APS_TOUCH_H +#define LGD_TOUCH_NAME "lgd_melfas_incell_touch" + +struct aps_ts_platform_data { + int max_x; + int max_y; + + int gpio_irq; + int gpio_reset; + unsigned int irq_flags; + unsigned int reset_flags; + char *name; /* input drv name */ +}; + +#endif /* _LINUX_APS_TOUCH_H */ From c62534082e1222179a1ff816ac2c5a09fdef2bd3 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 19 Nov 2014 17:33:36 +0300 Subject: [PATCH 013/104] bma2x2: fix may be used uninitialized --- arch/arm/configs/w5ds_global_com_defconfig | 2 +- drivers/input/sensor/bma2x2_driver.c | 277 +++++++++++---------- 2 files changed, 140 insertions(+), 139 deletions(-) diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index 216c9287c56d..0b350cebf975 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -285,7 +285,7 @@ CONFIG_INPUT_EVDEV=y CONFIG_INPUT_EVBUG=m CONFIG_KEYBOARD_GPIO=y CONFIG_INPUT_SENSOR=y -# CONFIG_SENSORS_BMA2X2 is not set +CONFIG_SENSORS_BMA2X2=y CONFIG_SENSORS_BMM050=y CONFIG_SENSOR_APDS9130=y CONFIG_INPUT_TOUCHSCREEN=y diff --git a/drivers/input/sensor/bma2x2_driver.c b/drivers/input/sensor/bma2x2_driver.c index 15322f49f068..511f91513469 100644 --- a/drivers/input/sensor/bma2x2_driver.c +++ b/drivers/input/sensor/bma2x2_driver.c @@ -1486,7 +1486,7 @@ static int bma2x2_set_int1_pad_sel(struct i2c_client *client, unsigned char int1sel) { int comres = 0; - unsigned char data; + unsigned char data = '0'; unsigned char state; state = 0x01; @@ -1569,7 +1569,7 @@ static int bma2x2_set_int2_pad_sel(struct i2c_client *client, unsigned char int2sel) { int comres = 0; - unsigned char data; + unsigned char data = '0'; unsigned char state; state = 0x01; @@ -1651,7 +1651,7 @@ static int bma2x2_set_Int_Enable(struct i2c_client *client, unsigned char InterruptType , unsigned char value) { int comres = 0; - unsigned char data1, data2; + unsigned char data1 = '0', data2 = '0'; if ((11 < InterruptType) && (InterruptType < 16)) { switch (InterruptType) { @@ -1786,7 +1786,7 @@ static int bma2x2_get_interruptstatus1(struct i2c_client *client, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS1_REG, &data); *intstatus = data; @@ -1799,7 +1799,7 @@ static int bma2x2_get_HIGH_first(struct i2c_client *client, unsigned char param, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; switch (param) { case 0: @@ -1831,7 +1831,7 @@ static int bma2x2_get_HIGH_sign(struct i2c_client *client, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, &data); @@ -1846,7 +1846,7 @@ static int bma2x2_get_slope_first(struct i2c_client *client, unsigned char param, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; switch (param) { case 0: @@ -1878,7 +1878,7 @@ static int bma2x2_get_slope_sign(struct i2c_client *client, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_TAP_SLOPE_REG, &data); @@ -1893,7 +1893,7 @@ static int bma2x2_get_orient_status(struct i2c_client *client, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, &data); @@ -1907,7 +1907,7 @@ static int bma2x2_get_orient_flat_status(struct i2c_client *client, unsigned char *intstatus) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG, &data); @@ -1921,7 +1921,7 @@ static int bma2x2_get_orient_flat_status(struct i2c_client *client, unsigned static int bma2x2_set_slope_source(struct i2c_client *client, unsigned char value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_UNFILT_INT_SRC_SLOPE__REG, &data); @@ -1936,7 +1936,7 @@ static int bma2x2_set_slope_source(struct i2c_client *client, unsigned char valu static int bma2x2_get_slope_source(struct i2c_client *client, unsigned char *value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_UNFILT_INT_SRC_SLOPE__REG, &data); @@ -1950,7 +1950,7 @@ static int bma2x2_get_slope_source(struct i2c_client *client, unsigned char *val static int bma2x2_set_high_g_source(struct i2c_client *client, unsigned char value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_UNFILT_INT_SRC_HIGHG__REG, &data); @@ -1964,7 +1964,7 @@ static int bma2x2_set_high_g_source(struct i2c_client *client, unsigned char val static int bma2x2_get_high_g_source(struct i2c_client *client, unsigned char *value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_UNFILT_INT_SRC_HIGHG__REG, &data); @@ -1977,7 +1977,7 @@ static int bma2x2_get_high_g_source(struct i2c_client *client, unsigned char *va static int bma2x2_set_high_g_hyst(struct i2c_client *client, unsigned char value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_HYST__REG, &data); @@ -1991,7 +1991,7 @@ static int bma2x2_set_high_g_hyst(struct i2c_client *client, unsigned char value static int bma2x2_get_high_g_hyst(struct i2c_client *client, unsigned char *value) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_HYST__REG, &data); @@ -2005,7 +2005,7 @@ static int bma2x2_get_high_g_hyst(struct i2c_client *client, unsigned char *valu static int bma2x2_set_Int_Mode(struct i2c_client *client, unsigned char Mode) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2022,7 +2022,7 @@ static int bma2x2_set_Int_Mode(struct i2c_client *client, unsigned char Mode) static int bma2x2_get_Int_Mode(struct i2c_client *client, unsigned char *Mode) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2037,7 +2037,7 @@ static int bma2x2_set_slope_duration(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2054,7 +2054,7 @@ static int bma2x2_get_slope_duration(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2070,7 +2070,7 @@ static int bma2x2_set_slope_no_mot_duration(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2087,7 +2087,7 @@ static int bma2x2_get_slope_no_mot_duration(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2103,7 +2103,7 @@ static int bma2x2_set_slope_threshold(struct i2c_client *client, unsigned char threshold) { int comres = 0; - unsigned char data; + unsigned char data = '0'; data = threshold; comres = bma2x2_smbus_write_byte(client, @@ -2116,7 +2116,7 @@ static int bma2x2_get_slope_threshold(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2130,7 +2130,7 @@ static int bma2x2_set_slope_no_mot_threshold(struct i2c_client *client, unsigned char threshold) { int comres = 0; - unsigned char data; + unsigned char data = '0'; data = threshold; comres = bma2x2_smbus_write_byte(client, @@ -2143,7 +2143,7 @@ static int bma2x2_get_slope_no_mot_threshold(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, @@ -2158,7 +2158,7 @@ static int bma2x2_set_low_g_duration(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_DUR__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_DUR, duration); @@ -2171,7 +2171,7 @@ static int bma2x2_get_low_g_duration(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_DURN_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_DUR); @@ -2184,7 +2184,7 @@ static int bma2x2_set_low_g_threshold(struct i2c_client *client, unsigned char threshold) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_THRES__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_THRES, threshold); @@ -2197,7 +2197,7 @@ static int bma2x2_get_low_g_threshold(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_THRES_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_THRES); @@ -2210,7 +2210,7 @@ static int bma2x2_set_high_g_duration(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_DUR__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_DUR, duration); @@ -2223,7 +2223,7 @@ static int bma2x2_get_high_g_duration(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_DURN_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_DUR); @@ -2236,7 +2236,7 @@ static int bma2x2_set_high_g_threshold(struct i2c_client *client, unsigned char threshold) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_THRES__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_THRES, threshold); @@ -2250,7 +2250,7 @@ static int bma2x2_get_high_g_threshold(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_THRES_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_THRES); @@ -2264,7 +2264,7 @@ static int bma2x2_set_tap_duration(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_DUR__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_DUR, duration); @@ -2277,7 +2277,7 @@ static int bma2x2_get_tap_duration(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_DUR); @@ -2289,7 +2289,7 @@ static int bma2x2_get_tap_duration(struct i2c_client *client, unsigned char static int bma2x2_set_tap_shock(struct i2c_client *client, unsigned char setval) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SHOCK_DURN__REG, &data); @@ -2304,7 +2304,7 @@ static int bma2x2_get_tap_shock(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SHOCK_DURN); @@ -2317,7 +2317,7 @@ static int bma2x2_set_tap_quiet(struct i2c_client *client, unsigned char duration) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_QUIET_DURN__REG, &data); @@ -2332,7 +2332,7 @@ static int bma2x2_get_tap_quiet(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_QUIET_DURN); @@ -2345,7 +2345,7 @@ static int bma2x2_set_tap_threshold(struct i2c_client *client, unsigned char threshold) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_THRES, threshold); @@ -2358,7 +2358,7 @@ static int bma2x2_get_tap_threshold(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_THRES); @@ -2370,7 +2370,7 @@ static int bma2x2_get_tap_threshold(struct i2c_client *client, unsigned char static int bma2x2_set_tap_samp(struct i2c_client *client, unsigned char samp) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SAMPLES__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_SAMPLES, samp); @@ -2383,7 +2383,7 @@ static int bma2x2_set_tap_samp(struct i2c_client *client, unsigned char samp) static int bma2x2_get_tap_samp(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SAMPLES); @@ -2395,7 +2395,7 @@ static int bma2x2_get_tap_samp(struct i2c_client *client, unsigned char *status) static int bma2x2_set_orient_mode(struct i2c_client *client, unsigned char mode) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_MODE__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_MODE, mode); @@ -2409,7 +2409,7 @@ static int bma2x2_get_orient_mode(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_MODE); @@ -2422,7 +2422,7 @@ static int bma2x2_set_orient_blocking(struct i2c_client *client, unsigned char samp) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_BLOCK__REG, &data); @@ -2437,7 +2437,7 @@ static int bma2x2_get_orient_blocking(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_BLOCK); @@ -2450,7 +2450,7 @@ static int bma2x2_set_orient_hyst(struct i2c_client *client, unsigned char orienthyst) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_HYST__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_HYST, orienthyst); @@ -2464,7 +2464,7 @@ static int bma2x2_get_orient_hyst(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_HYST); @@ -2476,7 +2476,7 @@ static int bma2x2_set_theta_blocking(struct i2c_client *client, unsigned char thetablk) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_BLOCK, thetablk); @@ -2490,7 +2490,7 @@ static int bma2x2_get_theta_blocking(struct i2c_client *client, unsigned char *status) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_BLOCK); @@ -2503,7 +2503,7 @@ static int bma2x2_set_theta_flat(struct i2c_client *client, unsigned char thetaflat) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT__REG, &data); data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_FLAT, thetaflat); @@ -2516,7 +2516,7 @@ static int bma2x2_get_theta_flat(struct i2c_client *client, unsigned char *status) { int comres = 0 ; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT_REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_FLAT); @@ -2529,7 +2529,7 @@ static int bma2x2_set_flat_hold_time(struct i2c_client *client, unsigned char holdtime) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME__REG, &data); @@ -2544,7 +2544,7 @@ static int bma2x2_get_flat_hold_time(struct i2c_client *client, unsigned char *holdtime) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME_REG, &data); @@ -2557,7 +2557,7 @@ static int bma2x2_get_flat_hold_time(struct i2c_client *client, unsigned char static int bma2x2_set_mode(struct i2c_client *client, unsigned char Mode) { int comres = 0; - unsigned char data1, data2; + unsigned char data1 = '0', data2 = '0'; if (Mode < 6) { comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG, @@ -2645,7 +2645,7 @@ static int bma2x2_set_mode(struct i2c_client *client, unsigned char Mode) static int bma2x2_get_mode(struct i2c_client *client, unsigned char *Mode) { int comres = 0; - unsigned char data1, data2; + unsigned char data1 = '0', data2 ='0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG, &data1); comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_NOISE_CTRL_REG, @@ -2692,7 +2692,7 @@ static int bma2x2_get_mode(struct i2c_client *client, unsigned char *Mode) static int bma2x2_set_range(struct i2c_client *client, unsigned char Range) { int comres = 0 ; - unsigned char data1; + unsigned char data1 = '0'; if ((Range == 3) || (Range == 5) || (Range == 8) || (Range == 12)) { comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL_REG, @@ -2729,7 +2729,7 @@ static int bma2x2_set_range(struct i2c_client *client, unsigned char Range) static int bma2x2_get_range(struct i2c_client *client, unsigned char *Range) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL__REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_RANGE_SEL); @@ -2742,7 +2742,7 @@ static int bma2x2_get_range(struct i2c_client *client, unsigned char *Range) static int bma2x2_set_bandwidth(struct i2c_client *client, unsigned char BW) { int comres = 0; - unsigned char data; + unsigned char data = '0'; int Bandwidth = 0; if (BW > 7 && BW < 16) { @@ -2805,7 +2805,7 @@ static int bma2x2_set_bandwidth(struct i2c_client *client, unsigned char BW) static int bma2x2_get_bandwidth(struct i2c_client *client, unsigned char *BW) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_BANDWIDTH__REG, &data); data = BMA2X2_GET_BITSLICE(data, BMA2X2_BANDWIDTH); @@ -2818,7 +2818,7 @@ int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char *sleep_dur) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_SLEEP_DUR__REG, &data); @@ -2832,7 +2832,7 @@ int bma2x2_set_sleep_duration(struct i2c_client *client, unsigned char sleep_dur) { int comres = 0; - unsigned char data; + unsigned char data = '0'; int sleep_duration = 0; if (sleep_dur > 4 && sleep_dur < 16) { @@ -2913,7 +2913,7 @@ static int bma2x2_get_fifo_mode(struct i2c_client *client, unsigned char *fifo_mode) { int comres; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_MODE__REG, &data); *fifo_mode = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_MODE); @@ -2924,7 +2924,7 @@ static int bma2x2_get_fifo_mode(struct i2c_client *client, unsigned char static int bma2x2_set_fifo_mode(struct i2c_client *client, unsigned char fifo_mode) { - unsigned char data; + unsigned char data = '0'; int comres = 0; if (fifo_mode < 4) { @@ -2945,7 +2945,7 @@ static int bma2x2_get_fifo_trig(struct i2c_client *client, unsigned char *fifo_trig) { int comres; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_TRIGGER_ACTION__REG, &data); @@ -2957,7 +2957,7 @@ static int bma2x2_get_fifo_trig(struct i2c_client *client, unsigned char static int bma2x2_set_fifo_trig(struct i2c_client *client, unsigned char fifo_trig) { - unsigned char data; + unsigned char data = '0'; int comres = 0; if (fifo_trig < 4) { @@ -2978,7 +2978,7 @@ static int bma2x2_get_fifo_trig_src(struct i2c_client *client, unsigned char *trig_src) { int comres; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data); @@ -2991,7 +2991,7 @@ static int bma2x2_get_fifo_trig_src(struct i2c_client *client, unsigned char static int bma2x2_set_fifo_trig_src(struct i2c_client *client, unsigned char trig_src) { - unsigned char data; + unsigned char data = '0'; int comres = 0; if (trig_src < 4) { @@ -3012,7 +3012,7 @@ static int bma2x2_get_fifo_framecount(struct i2c_client *client, unsigned char *framecount) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_FRAME_COUNTER_S__REG, &data); @@ -3025,7 +3025,7 @@ static int bma2x2_get_fifo_data_sel(struct i2c_client *client, unsigned char *data_sel) { int comres; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_DATA_SELECT__REG, &data); @@ -3037,7 +3037,7 @@ static int bma2x2_get_fifo_data_sel(struct i2c_client *client, unsigned char static int bma2x2_set_fifo_data_sel(struct i2c_client *client, unsigned char data_sel) { - unsigned char data; + unsigned char data = '0'; int comres = 0; if (data_sel < 4) { @@ -3059,7 +3059,7 @@ static int bma2x2_set_fifo_data_sel(struct i2c_client *client, unsigned char static int bma2x2_get_fifo_data_out_reg(struct i2c_client *client, unsigned char *out_reg) { - unsigned char data; + unsigned char data = '0'; int comres = 0; comres = bma2x2_smbus_read_byte(client, @@ -3073,7 +3073,7 @@ static int bma2x2_get_fifo_data_out_reg(struct i2c_client *client, unsigned char static int bma2x2_get_offset_target(struct i2c_client *client, unsigned char channel, unsigned char *offset) { - unsigned char data; + unsigned char data = '0'; int comres = 0; switch (channel) { @@ -3112,7 +3112,7 @@ static int bma2x2_get_offset_target(struct i2c_client *client, unsigned char static int bma2x2_set_offset_target(struct i2c_client *client, unsigned char channel, unsigned char offset) { - unsigned char data; + unsigned char data = '0'; int comres = 0; switch (channel) { @@ -3169,7 +3169,7 @@ static int bma2x2_get_cal_ready(struct i2c_client *client, unsigned char *calrdy ) { int comres = 0 ; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_FAST_CAL_RDY_S__REG, &data); @@ -3183,7 +3183,7 @@ static int bma2x2_set_cal_trigger(struct i2c_client *client, unsigned char caltrigger) { int comres = 0; - unsigned char data; + unsigned char data = '0'; #ifdef BMA2X2_ACCEL_CALIBRATION struct bma2x2_data *bma2x2 = i2c_get_clientdata(client); @@ -3216,7 +3216,7 @@ static int bma2x2_set_offset_x(struct i2c_client *client, unsigned char offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; data = offsetfilt; comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_X_AXIS_REG, @@ -3230,7 +3230,7 @@ static int bma2x2_get_offset_x(struct i2c_client *client, unsigned char *offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_X_AXIS_REG, &data); @@ -3243,7 +3243,7 @@ static int bma2x2_set_offset_y(struct i2c_client *client, unsigned char offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; data = offsetfilt; comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Y_AXIS_REG, @@ -3256,7 +3256,7 @@ static int bma2x2_get_offset_y(struct i2c_client *client, unsigned char *offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Y_AXIS_REG, &data); @@ -3269,7 +3269,7 @@ static int bma2x2_set_offset_z(struct i2c_client *client, unsigned char offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; data = offsetfilt; comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Z_AXIS_REG, @@ -3282,7 +3282,7 @@ static int bma2x2_get_offset_z(struct i2c_client *client, unsigned char *offsetfilt) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Z_AXIS_REG, &data); @@ -3296,7 +3296,7 @@ static int bma2x2_set_selftest_st(struct i2c_client *client, unsigned char selftest) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_EN_SELF_TEST__REG, &data); @@ -3310,7 +3310,7 @@ static int bma2x2_set_selftest_st(struct i2c_client *client, unsigned char static int bma2x2_set_selftest_stn(struct i2c_client *client, unsigned char stn) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_NEG_SELF_TEST__REG, &data); @@ -3324,7 +3324,7 @@ static int bma2x2_set_selftest_stn(struct i2c_client *client, unsigned char stn) static int bma2x2_set_selftest_amp(struct i2c_client *client, unsigned char amp) { int comres = 0; - unsigned char data; + unsigned char data = '0'; comres = bma2x2_smbus_read_byte(client, BMA2X2_SELF_TEST_AMP__REG, &data); @@ -3539,7 +3539,7 @@ static int bma2x2_read_accel_z(struct i2c_client *client, static int bma2x2_read_temperature(struct i2c_client *client, signed char *temperature) { - unsigned char data; + unsigned char data = '0'; int comres = 0; comres = bma2x2_smbus_read_byte(client, BMA2X2_TEMPERATURE_REG, &data); @@ -3623,7 +3623,7 @@ static ssize_t bma2x2_enable_int_store(struct device *dev, static ssize_t bma2x2_int_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_Int_Mode(bma2x2->bma2x2_client, &data) < 0) @@ -3652,7 +3652,7 @@ static ssize_t bma2x2_int_mode_store(struct device *dev, static ssize_t bma2x2_slope_duration_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_slope_duration(bma2x2->bma2x2_client, &data) < 0) @@ -3684,7 +3684,7 @@ static ssize_t bma2x2_slope_duration_store(struct device *dev, static ssize_t bma2x2_slope_no_mot_duration_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_slope_no_mot_duration(bma2x2->bma2x2_client, &data) < 0) @@ -3717,7 +3717,7 @@ static ssize_t bma2x2_slope_no_mot_duration_store(struct device *dev, static ssize_t bma2x2_slope_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_slope_threshold(bma2x2->bma2x2_client, &data) < 0) @@ -3748,7 +3748,7 @@ static ssize_t bma2x2_slope_threshold_store(struct device *dev, static ssize_t bma2x2_slope_no_mot_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_slope_no_mot_threshold(bma2x2->bma2x2_client, &data) < 0) @@ -3779,7 +3779,7 @@ static ssize_t bma2x2_slope_no_mot_threshold_store(struct device *dev, static ssize_t bma2x2_high_g_duration_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_high_g_duration(bma2x2->bma2x2_client, &data) < 0) @@ -3811,7 +3811,7 @@ static ssize_t bma2x2_high_g_duration_store(struct device *dev, static ssize_t bma2x2_high_g_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_high_g_threshold(bma2x2->bma2x2_client, &data) < 0) @@ -3842,7 +3842,7 @@ static ssize_t bma2x2_high_g_threshold_store(struct device *dev, static ssize_t bma2x2_low_g_duration_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_low_g_duration(bma2x2->bma2x2_client, &data) < 0) @@ -3874,7 +3874,7 @@ static ssize_t bma2x2_low_g_duration_store(struct device *dev, static ssize_t bma2x2_low_g_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_low_g_threshold(bma2x2->bma2x2_client, &data) < 0) @@ -3904,7 +3904,7 @@ static ssize_t bma2x2_low_g_threshold_store(struct device *dev, static ssize_t bma2x2_tap_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_tap_threshold(bma2x2->bma2x2_client, &data) < 0) @@ -3934,7 +3934,7 @@ static ssize_t bma2x2_tap_threshold_store(struct device *dev, static ssize_t bma2x2_tap_duration_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_tap_duration(bma2x2->bma2x2_client, &data) < 0) @@ -3965,7 +3965,7 @@ static ssize_t bma2x2_tap_duration_store(struct device *dev, static ssize_t bma2x2_tap_quiet_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_tap_quiet(bma2x2->bma2x2_client, &data) < 0) @@ -3997,7 +3997,7 @@ static ssize_t bma2x2_tap_quiet_store(struct device *dev, static ssize_t bma2x2_tap_shock_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_tap_shock(bma2x2->bma2x2_client, &data) < 0) @@ -4029,7 +4029,7 @@ static ssize_t bma2x2_tap_shock_store(struct device *dev, static ssize_t bma2x2_tap_samp_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_tap_samp(bma2x2->bma2x2_client, &data) < 0) @@ -4060,7 +4060,7 @@ static ssize_t bma2x2_tap_samp_store(struct device *dev, static ssize_t bma2x2_orient_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_orient_mode(bma2x2->bma2x2_client, &data) < 0) @@ -4092,7 +4092,7 @@ static ssize_t bma2x2_orient_mode_store(struct device *dev, static ssize_t bma2x2_orient_blocking_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_orient_blocking(bma2x2->bma2x2_client, &data) < 0) @@ -4123,7 +4123,7 @@ static ssize_t bma2x2_orient_blocking_store(struct device *dev, static ssize_t bma2x2_orient_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_orient_hyst(bma2x2->bma2x2_client, &data) < 0) @@ -4155,7 +4155,7 @@ static ssize_t bma2x2_orient_hyst_store(struct device *dev, static ssize_t bma2x2_orient_theta_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_theta_blocking(bma2x2->bma2x2_client, &data) < 0) @@ -4187,7 +4187,7 @@ static ssize_t bma2x2_orient_theta_store(struct device *dev, static ssize_t bma2x2_flat_theta_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_theta_flat(bma2x2->bma2x2_client, &data) < 0) @@ -4218,7 +4218,7 @@ static ssize_t bma2x2_flat_theta_store(struct device *dev, static ssize_t bma2x2_flat_hold_time_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_flat_hold_time(bma2x2->bma2x2_client, &data) < 0) @@ -4688,7 +4688,7 @@ static ssize_t bma2x2_register_show(struct device *dev, static ssize_t bma2x2_range_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_range(bma2x2->bma2x2_client, &data) < 0) @@ -4717,7 +4717,7 @@ static ssize_t bma2x2_range_store(struct device *dev, static ssize_t bma2x2_bandwidth_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_bandwidth(bma2x2->bma2x2_client, &data) < 0) @@ -4764,7 +4764,7 @@ static ssize_t bma2x2_bandwidth_store(struct device *dev, static ssize_t bma2x2_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_mode(bma2x2->bma2x2_client, &data) < 0) @@ -5199,7 +5199,7 @@ static ssize_t bma2x2_fast_calibration_x_show(struct device *dev, #ifdef BMA2X2_ACCEL_CALIBRATION return sprintf(buf, "%d\n", atomic_read(&bma2x2->fast_calib_x_rslt)); #else - unsigned char data; + unsigned char data = '0'; if (bma2x2_get_offset_target(bma2x2->bma2x2_client, 1, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5298,7 +5298,7 @@ static ssize_t bma2x2_fast_calibration_y_show(struct device *dev, #ifdef BMA2X2_ACCEL_CALIBRATION return sprintf(buf, "%d\n", atomic_read(&bma2x2->fast_calib_y_rslt)); #else - unsigned char data; + unsigned char data = '0'; if (bma2x2_get_offset_target(bma2x2->bma2x2_client, 2, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5399,7 +5399,7 @@ static ssize_t bma2x2_fast_calibration_z_show(struct device *dev, #ifdef BMA2X2_ACCEL_CALIBRATION return sprintf(buf, "%d\n", atomic_read(&bma2x2->fast_calib_z_rslt)); #else - unsigned char data; + unsigned char data = '0'; if (bma2x2_get_offset_target(bma2x2->bma2x2_client, 3, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5494,7 +5494,7 @@ static ssize_t bma2x2_fast_calibration_z_store(struct device *dev, static ssize_t bma2x2_SleepDur_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_sleep_duration(bma2x2->bma2x2_client, &data) < 0) @@ -5525,7 +5525,7 @@ static ssize_t bma2x2_SleepDur_store(struct device *dev, static ssize_t bma2x2_fifo_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_fifo_mode(bma2x2->bma2x2_client, &data) < 0) @@ -5558,7 +5558,7 @@ static ssize_t bma2x2_fifo_mode_store(struct device *dev, static ssize_t bma2x2_fifo_trig_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_fifo_trig(bma2x2->bma2x2_client, &data) < 0) @@ -5591,7 +5591,7 @@ static ssize_t bma2x2_fifo_trig_store(struct device *dev, static ssize_t bma2x2_fifo_trig_src_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_fifo_trig_src(bma2x2->bma2x2_client, &data) < 0) @@ -5624,7 +5624,7 @@ static ssize_t bma2x2_fifo_trig_src_store(struct device *dev, static ssize_t bma2x2_fifo_data_sel_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_fifo_data_sel(bma2x2->bma2x2_client, &data) < 0) @@ -5637,7 +5637,7 @@ static ssize_t bma2x2_fifo_data_sel_show(struct device *dev, static ssize_t bma2x2_fifo_framecount_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_fifo_framecount(bma2x2->bma2x2_client, &data) < 0) @@ -5650,7 +5650,7 @@ static ssize_t bma2x2_fifo_framecount_show(struct device *dev, static ssize_t bma2x2_temperature_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_read_temperature(bma2x2->bma2x2_client, &data) < 0) @@ -5683,7 +5683,7 @@ static ssize_t bma2x2_fifo_data_sel_store(struct device *dev, static ssize_t bma2x2_fifo_data_out_frame_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; int err, i, len; signed char fifo_data_out[MAX_FIFO_F_LEVEL * MAX_FIFO_F_BYTES] = {0}; unsigned char f_count, f_len = 0; @@ -5742,7 +5742,7 @@ static ssize_t bma2x2_fifo_data_out_frame_show(struct device *dev, static ssize_t bma2x2_offset_x_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_offset_x(bma2x2->bma2x2_client, &data) < 0) @@ -5774,7 +5774,7 @@ static ssize_t bma2x2_offset_x_store(struct device *dev, static ssize_t bma2x2_offset_y_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_offset_y(bma2x2->bma2x2_client, &data) < 0) @@ -5806,7 +5806,7 @@ static ssize_t bma2x2_offset_y_store(struct device *dev, static ssize_t bma2x2_offset_z_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_offset_z(bma2x2->bma2x2_client, &data) < 0) @@ -5838,7 +5838,7 @@ static ssize_t bma2x2_offset_z_store(struct device *dev, static ssize_t bma2x2_high_g_source_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_high_g_source(bma2x2->bma2x2_client, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5869,7 +5869,7 @@ static ssize_t bma2x2_high_g_source_store(struct device *dev, static ssize_t bma2x2_slope_source_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_slope_source(bma2x2->bma2x2_client, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5882,13 +5882,13 @@ static ssize_t bma2x2_slope_source_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long data; - int error; + unsigned char data = '0'; +// int error; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); - error = strict_strtoul(buf, 10, &data); - if (error) - return error; +// error = strict_strtoul(buf, 10, &data); +// if (error) +// return error; if (bma2x2_set_slope_source(bma2x2->bma2x2_client, (unsigned char)data) < 0) return -EINVAL; @@ -5899,7 +5899,7 @@ static ssize_t bma2x2_slope_source_store(struct device *dev, static ssize_t bma2x2_high_g_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned char data; + unsigned char data = '0'; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); if (bma2x2_get_high_g_hyst(bma2x2->bma2x2_client, &data) < 0) return sprintf(buf, "Read error\n"); @@ -5912,15 +5912,16 @@ static ssize_t bma2x2_high_g_hyst_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long data; + unsigned long data = 1; + unsigned char data1 = '0'; int error; struct bma2x2_data *bma2x2 = dev_get_drvdata(dev); error = strict_strtoul(buf, 10, &data); if (error) return error; - - if (bma2x2_set_high_g_hyst(bma2x2->bma2x2_client, (unsigned char)data) < 0) + data1 = (unsigned char)data; + if (bma2x2_set_high_g_hyst(bma2x2->bma2x2_client, data1) < 0) return -EINVAL; return count; @@ -6052,7 +6053,7 @@ static int bma2x2_read_Calibration_data(struct i2c_client *client) static int bma2x2_set_offset_target_fast_cal(struct i2c_client *client, unsigned char offset) { - unsigned char data; + unsigned char data = '0'; int comres = 0; @@ -6079,7 +6080,7 @@ static int bma2x2_set_offset_target_fast_cal(struct i2c_client *client, unsigned static int bma2x2_set_cal_trigger_fast_cal(struct i2c_client *client) { int comres = 0; - unsigned char data; + unsigned char data = '0'; signed char tmp; unsigned char timeout = 0; From df7c1f5466d039f814968bce2a0f764910696ec9 Mon Sep 17 00:00:00 2001 From: Chandan Gera Date: Thu, 5 Dec 2013 15:55:35 +0530 Subject: [PATCH 014/104] msm-camera: Fixed MIPI raw snaspshot issue 8x10 Removed clearing of interrupt mask register after the streamon is done add a reset logic for VFE after all the streams are inactive Change-Id: Ib8999baae8f75498dcc813aa01fbb44f8c104e94 CRs-Fixed: 583125 Signed-off-by: Chandan Gera Conflicts: drivers/media/platform/msm/camera_v2/isp/msm_isp32.c --- .../media/platform/msm/camera_v2/isp/msm_isp32.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index e0c32417a889..3065f625ead2 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -154,6 +154,13 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); //Aravind msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18); //Aravind + + msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); + //Aravind start vfe_dev->error_info.error_mask1 =0; vfe_dev->error_info.error_mask0 =0; @@ -161,8 +168,13 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) vfe_dev->error_info.violation_status =0; //Aravind end #else - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); - msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); + msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); #endif } From 412d1a7e5a95239b206de3e7d8d23601004677e9 Mon Sep 17 00:00:00 2001 From: Azam Sadiq Pasha Kapatrala Syed Date: Thu, 6 Feb 2014 19:13:07 -0800 Subject: [PATCH 015/104] msm: camera: Cleanup msm_generic_buff list while close Cleanup the camera msm generic buffer manager list when all v4l2 nodes are closed. Change-Id: I27636533621d3329bcc5dffba8c003d2cdc252c2 CRs-Fixed: 599983 Signed-off-by: Azam Sadiq Pasha Kapatrala Syed --- drivers/media/platform/msm/camera_v2/msm.c | 11 ++++++++++- .../msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c | 6 +----- .../msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h | 3 +-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index b1a23b4713bb..10f94c934398 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,8 @@ #include "msm.h" #include "msm_vb2.h" #include "msm_sd.h" +#include + static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; @@ -504,6 +506,7 @@ static void msm_remove_session_cmd_ack_q(struct msm_session *session) int msm_destroy_session(unsigned int session_id) { struct msm_session *session; + struct v4l2_subdev *buf_mgr_subdev; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); @@ -515,6 +518,12 @@ int msm_destroy_session(unsigned int session_id) mutex_destroy(&session->lock); msm_delete_entry(msm_session_q, struct msm_session, list, session); + buf_mgr_subdev = msm_buf_mngr_get_subdev(); + if (buf_mgr_subdev) { + v4l2_subdev_call(buf_mgr_subdev, core, ioctl, + MSM_SD_SHUTDOWN, NULL); + } else + pr_err("%s: Buff manger device node is NULL\n", __func__); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index cb8fcdd8dde8..fd6bf59a1727 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -129,7 +129,6 @@ static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } - buf_mngr_dev->msm_buf_mngr_open_cnt++; return rc; } @@ -143,9 +142,6 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } - buf_mngr_dev->msm_buf_mngr_open_cnt--; - if (buf_mngr_dev->msm_buf_mngr_open_cnt == 0) - msm_buf_mngr_sd_shutdown(buf_mngr_dev); return rc; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index 49fad229ca0c..82ea21fcf208 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -36,6 +36,5 @@ struct msm_buf_mngr_device { spinlock_t buf_q_spinlock; struct msm_sd_subdev subdev; struct msm_sd_req_vb2_q vb2_ops; - uint32_t msm_buf_mngr_open_cnt; }; #endif From 88662accfff16d1d8e207fbd02605c2b7271947c Mon Sep 17 00:00:00 2001 From: Yonggui Mao Date: Wed, 13 Nov 2013 01:00:15 -0800 Subject: [PATCH 016/104] msm: camera2: add api for set actuator for manual af this is to support fr18291 manually set focus position Change-Id: Idec86177abc265c38fd48e18d1320c5ae40b7f03 Signed-off-by: Yongui Mao --- .../camera_v2/sensor/actuator/msm_actuator.c | 44 +++++++++++++++++++ .../camera_v2/sensor/actuator/msm_actuator.h | 2 + include/media/msm_cam_sensor.h | 10 +++++ 3 files changed, 56 insertions(+) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 706dbabd16c0..292ff07fd48f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -496,6 +496,43 @@ static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl) return rc; } +static int32_t msm_actuator_set_position( + struct msm_actuator_ctrl_t *a_ctrl, + struct msm_actuator_set_position_t *set_pos) +{ + int32_t rc = 0; + int32_t index; + uint16_t next_lens_position; + uint16_t delay; + uint32_t hw_params = 0; + struct msm_camera_i2c_reg_setting reg_setting; + CDBG("%s Enter %d\n", __func__, __LINE__); + if (set_pos->number_of_steps == 0) + return rc; + + a_ctrl->i2c_tbl_index = 0; + for (index = 0; index < set_pos->number_of_steps; index++) { + next_lens_position = set_pos->pos[index]; + delay = set_pos->delay[index]; + a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, + next_lens_position, hw_params, delay); + + reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; + reg_setting.size = a_ctrl->i2c_tbl_index; + reg_setting.data_type = a_ctrl->i2c_data_type; + + rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay( + &a_ctrl->i2c_client, ®_setting); + if (rc < 0) { + pr_err("%s Failed I2C write Line %d\n", __func__, __LINE__); + return rc; + } + a_ctrl->i2c_tbl_index = 0; + } + CDBG("%s exit %d\n", __func__, __LINE__); + return rc; +} + static int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl, struct msm_actuator_set_info_t *set_info) { struct reg_settings_t *init_settings = NULL; @@ -653,6 +690,12 @@ static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl, pr_err("move focus failed %d\n", rc); break; + case CFG_SET_POSITION: + rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl, + &cdata->cfg.setpos); + if (rc < 0) + pr_err("actuator_set_position failed %d\n", rc); + break; default: break; } @@ -1154,6 +1197,7 @@ static struct msm_actuator msm_vcm_actuator_table = { .actuator_set_default_focus = msm_actuator_set_default_focus, .actuator_init_focus = msm_actuator_init_focus, .actuator_parse_i2c_params = msm_actuator_parse_i2c_params, + .actuator_set_position = msm_actuator_set_position, }, }; diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h index 3b29401b73d1..42a141ab3a7e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h @@ -48,6 +48,8 @@ struct msm_actuator_func_tbl { struct damping_params_t *, int8_t, int16_t); + int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *, + struct msm_actuator_set_position_t *); }; struct msm_actuator { diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 239dea222918..4c865363a77b 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -47,6 +47,7 @@ #define MAX_EEPROM_NAME 32 #define MAX_AF_ITERATIONS 3 +#define MAX_NUMBER_OF_STEPS 47 enum flash_type { LED_FLASH = 1, @@ -460,6 +461,7 @@ enum msm_actuator_cfg_type_t { CFG_GET_ACTUATOR_INFO, CFG_SET_ACTUATOR_INFO, CFG_SET_DEFAULT_FOCUS, + CFG_SET_POSITION, CFG_MOVE_FOCUS, }; @@ -557,6 +559,13 @@ enum af_camera_name { ACTUATOR_WEB_CAM_2, }; + +struct msm_actuator_set_position_t { + uint16_t number_of_steps; + uint16_t pos[MAX_NUMBER_OF_STEPS]; + uint16_t delay[MAX_NUMBER_OF_STEPS]; +}; + struct msm_actuator_cfg_data { int cfgtype; uint8_t is_af_supported; @@ -564,6 +573,7 @@ struct msm_actuator_cfg_data { struct msm_actuator_move_params_t move; struct msm_actuator_set_info_t set_info; struct msm_actuator_get_info_t get_info; + struct msm_actuator_set_position_t setpos; enum af_camera_name cam_name; } cfg; }; From eab1cd28afd7e78dab7eaf1cd725c6a01e4325aa Mon Sep 17 00:00:00 2001 From: Sreesudhan Ramakrish Ramkumar Date: Thu, 19 Dec 2013 11:51:26 -0800 Subject: [PATCH 017/104] msm: camera: Return current lens position in actuator driver Actuator user space driver requires lens position after move focus call returns. Return lens position as part cfg params for move focus ioctl. Change-Id: I8fdce16c192db8685b1a2ac66a2cba052d64423c Signed-off-by: Sreesudhan Ramakrish Ramkumar --- .../platform/msm/camera_v2/sensor/actuator/msm_actuator.c | 5 ++++- include/media/msm_cam_sensor.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 292ff07fd48f..4a2f8746486c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -291,6 +291,9 @@ static int32_t msm_actuator_move_focus( /* LGE_CHANGE_E, fix kernel sometimes crash while AF, 2013.11.4, yousung.kang@lge.com */ #endif + curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; + move_params->curr_lens_pos = curr_lens_pos; + if (copy_from_user(&ringing_params_kernel, &(move_params->ringing_params[a_ctrl->curr_region_index]), sizeof(struct damping_params_t))) { @@ -303,7 +306,6 @@ static int32_t msm_actuator_move_focus( if (dest_step_pos == a_ctrl->curr_step_pos) return rc; - curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; a_ctrl->i2c_tbl_index = 0; CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n", a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); @@ -342,6 +344,7 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos = target_step_pos; } + move_params->curr_lens_pos = curr_lens_pos; reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; reg_setting.data_type = a_ctrl->i2c_data_type; reg_setting.size = a_ctrl->i2c_tbl_index; diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 4c865363a77b..1b52914d922e 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -504,6 +504,7 @@ struct msm_actuator_move_params_t { int8_t sign_dir; int16_t dest_step_pos; int32_t num_steps; + uint16_t curr_lens_pos; struct damping_params_t *ringing_params; }; From fc7cfdb2f3ffd42aa20c51c1b4e96920868292ea Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 19 Nov 2014 20:17:58 +0300 Subject: [PATCH 018/104] camera: add LGE_CHANGE --- .../platform/msm/camera_v2/isp/msm_buf_mgr.c | 46 ++++++ .../platform/msm/camera_v2/isp/msm_isp_util.c | 67 +++++++++ .../platform/msm/camera_v2/ispif/msm_ispif.c | 27 +++- .../msm_buf_mgr/msm_generic_buf_mgr.c | 4 + .../msm_buf_mgr/msm_generic_buf_mgr.h | 1 + .../msm/camera_v2/pproc/cpp/msm_cpp.c | 77 +++++++++- .../msm/camera_v2/sensor/cci/msm_cci.c | 15 +- .../msm/camera_v2/sensor/eeprom/msm_eeprom.c | 133 +++++++++++++++++- 8 files changed, 358 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index a85f8536ce2d..6c772eed33b0 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -309,6 +309,11 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, struct msm_isp_buffer *temp_buf_info; struct msm_isp_bufq *bufq = NULL; struct vb2_buffer *vb2_buf = NULL; +/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ + struct buffer_cmd *buf_pending = NULL; + struct msm_isp_buffer_mapped_info *mped_info_tmp1; + struct msm_isp_buffer_mapped_info *mped_info_tmp2; +/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle); if (!bufq) { pr_err("%s: Invalid bufq\n", __func__); @@ -348,9 +353,30 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, list_for_each_entry(temp_buf_info, &bufq->head, list) { if (temp_buf_info->state == MSM_ISP_BUFFER_STATE_QUEUED) { +/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ +#if 0 //QCT original /* found one buf */ list_del_init(&temp_buf_info->list); *buf_info = temp_buf_info; +#else + + list_for_each_entry(buf_pending, &buf_mgr->buffer_q, list) { + if (!buf_pending) + break; + mped_info_tmp1 = buf_pending->mapped_info; + mped_info_tmp2 = &temp_buf_info->mapped_info[0]; + + if (mped_info_tmp1 == mped_info_tmp2 + && (mped_info_tmp1->len == mped_info_tmp2->len) + && (mped_info_tmp1->paddr == mped_info_tmp2->paddr)) { + /* found one buf */ + list_del_init(&temp_buf_info->list); + *buf_info = temp_buf_info; + break; + } + } +#endif +/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ break; } } @@ -359,9 +385,29 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, bufq->session_id, bufq->stream_id); if (vb2_buf) { if (vb2_buf->v4l2_buf.index < bufq->num_bufs) { +/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ +#if 0 //QCT Original *buf_info = &bufq->bufs[vb2_buf->v4l2_buf.index]; (*buf_info)->vb2_buf = vb2_buf; +#else + list_for_each_entry(buf_pending, &buf_mgr->buffer_q, list) { + if (!buf_pending) + break; + mped_info_tmp1 = buf_pending->mapped_info; + mped_info_tmp2 = + &bufq->bufs[vb2_buf->v4l2_buf.index].mapped_info[0]; + + if (mped_info_tmp1 == mped_info_tmp2 + && (mped_info_tmp1->len == mped_info_tmp2->len) + && (mped_info_tmp1->paddr == mped_info_tmp2->paddr)) { + *buf_info = &bufq->bufs[vb2_buf->v4l2_buf.index]; + (*buf_info)->vb2_buf = vb2_buf; + break; + } + } +#endif +/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ } else { pr_err("%s: Incorrect buf index %d\n", __func__, vb2_buf->v4l2_buf.index); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 734725101377..ee1876fdd33a 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -544,6 +544,8 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, reg_cfg_cmd->u.mask_info.reg_offset); break; } +/* LGE_CHANGE_S, jaehan.jeong, 2013.11.8, Applied QCT patch CN#01252253 - page fault in case of 64BIT_DMI, [STARTS HERE] */ +#if 0 //QCT Original case VFE_WRITE_DMI_16BIT: case VFE_WRITE_DMI_32BIT: case VFE_WRITE_DMI_64BIT: { @@ -592,6 +594,71 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, } break; } +#else + case VFE_WRITE_DMI_16BIT: + case VFE_WRITE_DMI_32BIT: { + int i; + uint32_t *lo_tbl_ptr = NULL; + uint32_t lo_val, lo_val1; + + if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset + + reg_cfg_cmd->u.dmi_info.len > cmd_len) { + pr_err("Invalid Lo Table out of bounds\n"); + return -EINVAL; + } + lo_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; + + for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) { + lo_val = *lo_tbl_ptr++; + if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) { + lo_val1 = lo_val & 0x0000FFFF; + lo_val = (lo_val & 0xFFFF0000)>>16; + msm_camera_io_w(lo_val1, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + } + msm_camera_io_w(lo_val, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + } + break; + } + case VFE_WRITE_DMI_64BIT: { + int i; + uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL; + uint32_t hi_val, lo_val; + + if (reg_cfg_cmd->u.dmi_info.hi_tbl_offset + + reg_cfg_cmd->u.dmi_info.len > cmd_len) { + pr_err("Invalid Hi Table out of bounds\n"); + return -EINVAL; + } + + if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset + + reg_cfg_cmd->u.dmi_info.len > cmd_len) { + pr_err("Invalid Lo Table out of bounds\n"); + return -EINVAL; + } + + hi_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4; + + lo_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; + + for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/8; i++) { + lo_val = *lo_tbl_ptr; + hi_val = *hi_tbl_ptr; + lo_tbl_ptr = lo_tbl_ptr + 2; + hi_tbl_ptr = hi_tbl_ptr + 2; + msm_camera_io_w(hi_val, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset); + msm_camera_io_w(lo_val, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + } + break; + } +#endif +/* LGE_CHANGE_E, jaehan.jeong, 2013.11.8, Applied QCT patch CN#01252253 - page fault in case of 64BIT_DMI, [ENDS HERE] */ case VFE_READ_DMI_16BIT: case VFE_READ_DMI_32BIT: case VFE_READ_DMI_64BIT: { diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 9a8963df0b76..cdb4094a888c 100755 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -97,13 +97,19 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) int rc = 0; long timeout = 0; struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)]; - +#if defined(CONFIG_HI351) + if(ispif->hw_num_isps > 1){ +#else + { +#endif +/*LGE CHANGE_E, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_reset_clk_info, reset_clk, ARRAY_SIZE(ispif_8974_reset_clk_info), 1); - if (rc < 0) { - pr_err("%s: cannot enable clock, error = %d", - __func__, rc); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); + } } init_completion(&ispif->reset_complete[VFE0]); @@ -160,6 +166,13 @@ static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable) /* Older ISPIF versiond don't need ahb clokc */ return 0; } +/*LGE CHANGE_S, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ +#if defined(CONFIG_HI351) + if(ispif->hw_num_isps > 1){ +#else + { +#endif +/*LGE CHANGE_E, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_ahb_clk_info, &ispif->ahb_clk, @@ -168,6 +181,7 @@ static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable) pr_err("%s: cannot enable clock, error = %d", __func__, rc); } + } return rc; } @@ -939,7 +953,10 @@ static int msm_ispif_init(struct ispif_device *ispif, pr_err("%s: ahb_clk enable failed", __func__); goto error_ahb; } - +/*LGE CHANGE_S, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ +#if defined(CONFIG_HI351) + msm_ispif_reset_hw(ispif); +#endif if (of_device_is_compatible(ispif->pdev->dev.of_node, "qcom,ispif-v3.0")) { /* currently HW reset is implemented for 8974 only */ diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index fd6bf59a1727..299de090b7be 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -129,6 +129,7 @@ static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } + buf_mngr_dev->msm_buf_mngr_open_cnt++; return rc; } @@ -142,6 +143,9 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } + buf_mngr_dev->msm_buf_mngr_open_cnt--; + if (buf_mngr_dev->msm_buf_mngr_open_cnt == 0) + msm_buf_mngr_sd_shutdown(buf_mngr_dev); return rc; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index 82ea21fcf208..b06d4da7a8fd 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -36,5 +36,6 @@ struct msm_buf_mngr_device { spinlock_t buf_q_spinlock; struct msm_sd_subdev subdev; struct msm_sd_req_vb2_q vb2_ops; + uint32_t msm_buf_mngr_open_cnt; /* LGE_CHANGE, jaehan.jeong, 2013.12.29, QCT PATCH, Cleanup msm generic buf queue handling */ }; #endif diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 10a0085ed58a..fa13b48b75e3 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -94,6 +94,23 @@ static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, spin_unlock_irqrestore(&__q->lock, flags); \ qcmd; \ }) +/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ +#define msm_cpp_empty_list(queue, member) { \ + unsigned long flags; \ + struct msm_queue_cmd *qcmd = NULL; \ + if (queue) { \ + spin_lock_irqsave(&queue->lock, flags); \ + while (!list_empty(&queue->list)) { \ + queue->len--; \ + qcmd = list_first_entry(&queue->list, \ + struct msm_queue_cmd, member); \ + list_del_init(&qcmd->member); \ + kfree(qcmd); \ + } \ + spin_unlock_irqrestore(&queue->lock, flags); \ + } \ +} +/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ static void msm_queue_init(struct msm_device_queue *queue, const char *name) { @@ -141,6 +158,20 @@ static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev); static void msm_cpp_write(u32 data, void __iomem *cpp_base) { +/* LGE_CHANGE_S, QCT patch for CPP, 2013-11-19, hyungtae.lee@lge.com */ + uint32_t tmp; + int num_tries=50; + do { + tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_RX_STAT); + num_tries --; + } while (((tmp & 0x1) == 0x0) && (num_tries > 0)); + + if(num_tries <= 0) { + pr_err("error: cant write, RX FIFO Full\n"); + return; + } +/* LGE_CHANGE_E, QCT patch for CPP, 2013-11-19, hyungtae.lee@lge.com */ + writel_relaxed((data), cpp_base + MSM_CPP_MICRO_FIFO_RX_DATA); } @@ -914,8 +945,23 @@ static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) uint32_t i; struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd); +/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + struct msm_device_queue *processing_q = NULL; + struct msm_device_queue *eventData_q = NULL; + + if (!cpp_dev) { + pr_err("failed: cpp_dev %p\n", cpp_dev); + return -EINVAL; + } +/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + mutex_lock(&cpp_dev->mutex); +/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + processing_q = &cpp_dev->processing_q; + eventData_q = &cpp_dev->eventData_q; +/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + if (cpp_dev->cpp_open_cnt == 0) { mutex_unlock(&cpp_dev->mutex); return 0; @@ -997,13 +1043,25 @@ static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev) { struct v4l2_event v4l2_evt; - struct msm_queue_cmd *frame_qcmd; - struct msm_queue_cmd *event_qcmd; - struct msm_cpp_frame_info_t *processed_frame; - struct msm_device_queue *queue = &cpp_dev->processing_q; +/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + struct msm_queue_cmd *frame_qcmd = NULL; + struct msm_queue_cmd *event_qcmd = NULL; + struct msm_cpp_frame_info_t *processed_frame = NULL; +/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + struct msm_device_queue *queue = &cpp_dev->processing_q; struct msm_buf_mngr_info buff_mgr_info; int rc = 0; +/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ +#if 0 + if (queue->len > 0) { + frame_qcmd = msm_dequeue(queue, list_frame); +#else + frame_qcmd = msm_dequeue(queue, list_frame); + if (frame_qcmd) { +#endif +/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ + if (queue->len > 0) { frame_qcmd = msm_dequeue(queue, list_frame); processed_frame = frame_qcmd->command; @@ -1295,6 +1353,16 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, new_frame->duplicate_identity); memset(&new_frame->output_buffer_info[1], 0, sizeof(struct msm_cpp_buffer_info_t)); +/* LGE_CHANGE_S, jaehan.jeong, 2013.12.29, Release vb2 buffer in cpp driver on error, [STARTS HERE] */ +#if 0 //QCT original + memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); + buff_mgr_info.session_id = + ((new_frame->duplicate_identity >> 16) & 0xFFFF); + buff_mgr_info.stream_id = + (new_frame->duplicate_identity & 0xFFFF); + rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, + &buff_mgr_info); +#else memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); dup_buff_mgr_info.session_id = ((new_frame->duplicate_identity >> 16) & 0xFFFF); @@ -1302,6 +1370,7 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, (new_frame->duplicate_identity & 0xFFFF); rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, &dup_buff_mgr_info); +#endif if (rc < 0) { rc = -EAGAIN; pr_debug("error getting buffer rc:%d\n", rc); diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index e072e53057b2..e68739e2669f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -29,7 +29,7 @@ #define CYCLES_PER_MICRO_SEC 4915 #define CCI_MAX_DELAY 10000 -#define CCI_TIMEOUT msecs_to_jiffies(100) +#define CCI_TIMEOUT msecs_to_jiffies(300) /* TODO move this somewhere else */ #define MSM_CCI_DRV_NAME "msm_cci" @@ -778,6 +778,7 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, struct msm_camera_cci_ctrl *cci_ctrl) { int32_t rc = 0; + int32_t trialCnt = 3; /*QCT_PATCH, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ CDBG("%s line %d cmd %d\n", __func__, __LINE__, cci_ctrl->cmd); switch (cci_ctrl->cmd) { @@ -791,6 +792,18 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, rc = msm_cci_i2c_read_bytes(sd, cci_ctrl); break; case MSM_CCI_I2C_WRITE: +/*QCT_PATCH E, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ +#if 1 // QCT Test + do{ + rc = msm_cci_i2c_write(sd, cci_ctrl); + if(rc < 0) + pr_err("%s: line %d trialCnt = %d \n", __func__, __LINE__, trialCnt); + trialCnt--; + }while(rc < 0 && trialCnt > 0); +#else + rc = msm_cci_i2c_write(sd, cci_ctrl); +#endif +/*QCT_PATCH E, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ rc = msm_cci_i2c_write(sd, cci_ctrl); break; case MSM_CCI_GPIO_WRITE: diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c index 69c1faa5dd48..845da3e69eb7 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c +++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c @@ -24,6 +24,9 @@ #define CDBG(fmt, args...) pr_debug(fmt, ##args) #endif +// [LGE_UPDATE] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) +char *lge_camera_info = NULL; + DEFINE_MSM_MUTEX(msm_eeprom_mutex); int32_t msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl, @@ -216,6 +219,42 @@ int32_t read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl) return rc; } memptr += emap[j].mem.valid_size; +// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up + /* + *We need to change slave address for read data (Y412B) + *I2C addr : 0x50, 0x51, 0x52, 0x53 + */ + #if !defined(CONFIG_ARCH_MSM8610) + #if defined(CONFIG_IMX111) + CDBG("%s: I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.cci_client->sid); + e_ctrl->i2c_client.cci_client->sid++; + #endif + #else + #if defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) + if(lge_camera_info){ + if(!strncmp(lge_camera_info, "imx111", 6)){ + CDBG("%s: (global)I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.client->addr); + e_ctrl->i2c_client.client->addr+=0x2; + } + } + #else + #if defined(CONFIG_IMX111) + CDBG("%s: I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.client->addr); + e_ctrl->i2c_client.client->addr+=0x2; + #endif + #endif + #endif +// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up + } + if (emap[j].pageen.valid_size) { + e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; + rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( + &(e_ctrl->i2c_client), emap[j].pageen.addr, + 0, emap[j].pageen.data_t); + if (rc < 0) { + pr_err("%s: page disable failed\n", __func__); + return rc; + } } if (emap[j].pageen.valid_size) { e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; @@ -389,13 +428,71 @@ static struct msm_cam_clk_info cam_8960_clk_info[] = { }; static struct msm_cam_clk_info cam_8974_clk_info[] = { - [SENSOR_CAM_MCLK] = {"cam_src_clk", 19200000}, +// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up + [SENSOR_CAM_MCLK] = {"cam_src_clk", 24000000}, +// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up [SENSOR_CAM_CLK] = {"cam_clk", 0}, }; static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = { .ioctl = msm_eeprom_subdev_ioctl, }; +// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up +#if defined(CONFIG_IMX111)|| defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) +static int msm_eeprom_check_CRC(struct msm_eeprom_ctrl_t *e_ctrl) +{ + int CRC_code, CRC_sum, counter; + + // (CRC MSB - 0x53, 0x7E << 8 ) + (CRC LSB - 0x53, 0x7F) = SUM( 0x50, 0x0A ~ 0x53,0x7D) + CRC_code = (e_ctrl->memory_data[0x037E] << 8) + e_ctrl->memory_data[0x037F]; + CRC_sum = 0; + + for( counter = 0x000A; counter <= 0x037D; counter++ ) { + CRC_sum += e_ctrl->memory_data[counter]; + } + + CRC_sum &= 0xffff; + + if( CRC_code != CRC_sum ) { + // CRC error + pr_err("%s IMX111 EEPROM CRC error for 5100K! CRC = 0x%04x / 0x%04x\n", __func__, CRC_code, CRC_sum); + return EIO; + } else { + CDBG("%s CRC for 5100K - OK = 0x%04x / 0x%04x", __func__, CRC_code, CRC_sum); + } + + // (CRC MSB - 0x53, 0xC4 << 8 ) + (CRC LSB - 0x53, 0xC5) = SUM( 0x53, 0x80 ~ 0x53,0xC3) + CRC_code = (e_ctrl->memory_data[0x03C4] << 8) + e_ctrl->memory_data[0x03C5]; + CRC_sum = 0; + + for( counter = 0x0380; counter <= 0x03C3; counter++ ) { + CRC_sum += e_ctrl->memory_data[counter]; + } + + CRC_sum &= 0xffff; + + if( CRC_code != CRC_sum ) { + // CRC error + pr_err("%s IMX111 EEPROM CRC error for 2856K! CRC = 0x%04x / 0x%04x\n", __func__, CRC_code, CRC_sum); + return EIO; + } else { + CDBG("%s CRC for 2856K - OK = 0x%04x / 0x%04x", __func__, CRC_code, CRC_sum); + } + + return 0; +} +#endif +// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up +// [LGE_UPDATE_S] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) +static int __init camera_information_setup(char *cam_info) +{ + lge_camera_info = cam_info; + printk(KERN_INFO "Camera Info : %s\n", lge_camera_info); + return 1; +} +__setup("lge.camera=", camera_information_setup); +// [LGE_UPDATE_E] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) + static struct v4l2_subdev_ops msm_eeprom_subdev_ops = { .core = &msm_eeprom_subdev_core_ops, @@ -444,7 +541,7 @@ int32_t msm_eeprom_i2c_probe(struct i2c_client *client, } power_info = &e_ctrl->eboard_info->power_info; - e_ctrl->eboard_info->i2c_slaveaddr = temp; + e_ctrl->eboard_info->i2c_slaveaddr = temp<<1; //LGE_UPDATE makes 8bit for i2c driver 2013-11-26 yt.jeon@lge.com e_ctrl->i2c_client.client = client; e_ctrl->is_supported = 0; @@ -490,6 +587,29 @@ int32_t msm_eeprom_i2c_probe(struct i2c_client *client, for (j = 0; j < e_ctrl->num_bytes; j++) CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]); + // [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up +#if defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) + if(lge_camera_info){ + if(!strncmp(lge_camera_info, "imx111", 6)){ + pr_err("%s (GLOBAL) call msm_eeprom_check_CRC\n", __func__); + rc = msm_eeprom_check_CRC(e_ctrl); + if (rc < 0) { + pr_err("%s read_eeprom_memory failed\n", __func__); + goto memdata_free; + } + } + } +#else + #if defined(CONFIG_IMX111) + pr_err("%s call msm_eeprom_check_CRC\n", __func__); + rc = msm_eeprom_check_CRC(e_ctrl); + if (rc < 0) { + pr_err("%s read_eeprom_memory failed\n", __func__); + goto memdata_free; + } + #endif +#endif + // [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); @@ -906,6 +1026,15 @@ static int32_t msm_eeprom_platform_probe(struct platform_device *pdev) pr_err("%s line %d\n", __func__, __LINE__); for (j = 0; j < e_ctrl->num_bytes; j++) CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]); + // [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up + #if defined(CONFIG_IMX111) + rc = msm_eeprom_check_CRC(e_ctrl); + if (rc < 0) { + pr_err("%s read_eeprom_memory failed\n", __func__); + goto memdata_free; + } + #endif + // [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); From 7492486ab880175e1b1d6e9a515fcbf330098303 Mon Sep 17 00:00:00 2001 From: Manikanta Sivapala Date: Thu, 5 Jun 2014 19:59:56 +0530 Subject: [PATCH 019/104] msm: vidc: Fix various decoder downscalar issues Queue output buffers without extradata to Venus. Venus is returning UPSCALE_NOT_SUPPORTED error when output2 size is bigger than output size. This change suppresses this error as it is not fatal. Change-Id: I8a3f708092b131400b88828f6c1a684f08b3f18a Signed-off-by: Manikanta Sivapala --- .../platform/msm/vidc/hfi_response_handler.c | 1 + .../media/platform/msm/vidc/msm_vidc_common.c | 23 +++++++++++-------- .../media/platform/msm/vidc/vidc_hfi_helper.h | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c index c6fb382b3581..0eee5308cf13 100644 --- a/drivers/media/platform/msm/vidc/hfi_response_handler.c +++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c @@ -221,6 +221,7 @@ static void hfi_process_session_error( case HFI_ERR_SESSION_INVALID_SCALE_FACTOR: case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE: case HFI_ERR_SESSION_UNSUPPORTED_SETTING: + case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED: dprintk(VIDC_INFO, "Non Fatal : HFI_EVENT_SESSION_ERROR\n"); break; default: diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 286b19782ca2..9fea431b8f12 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -2031,7 +2031,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst, int rc = 0; struct msm_smem *handle; struct internal_buf *binfo; - struct vidc_buffer_addr_info buffer_info; + struct vidc_buffer_addr_info buffer_info = {0}; u32 smem_flags = 0, buffer_size; struct hal_buffer_requirements *output_buf, *extradata_buf; int i; @@ -2051,19 +2051,21 @@ static int set_output_buffers(struct msm_vidc_inst *inst, output_buf->buffer_count_actual, output_buf->buffer_size); + buffer_size = output_buf->buffer_size; + extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); - if (!extradata_buf) { + if (extradata_buf) { + dprintk(VIDC_DBG, + "extradata: num = %d, size = %d\n", + extradata_buf->buffer_count_actual, + extradata_buf->buffer_size); + buffer_size += extradata_buf->buffer_size; + } else { dprintk(VIDC_DBG, "This extradata buffer not required, buffer_type: %x\n", buffer_type); - return 0; } - dprintk(VIDC_DBG, - "extradata: num = %d, size = %d\n", - extradata_buf->buffer_count_actual, - extradata_buf->buffer_size); - buffer_size = output_buf->buffer_size + extradata_buf->buffer_size; if (inst->flags & VIDC_SECURE) smem_flags |= SMEM_SECURE; @@ -2101,7 +2103,10 @@ static int set_output_buffers(struct msm_vidc_inst *inst, buffer_info.align_device_addr = handle->device_addr; buffer_info.extradata_addr = handle->device_addr + output_buf->buffer_size; - buffer_info.extradata_size = extradata_buf->buffer_size; + if (extradata_buf) { + buffer_info.extradata_size = + extradata_buf->buffer_size; + } dprintk(VIDC_DBG, "Output buffer address: %x", buffer_info.align_device_addr); dprintk(VIDC_DBG, "Output extradata address: %x", diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index 7c62e77ca59d..b3c20b657ccc 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -70,6 +70,7 @@ #define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE (HFI_COMMON_BASE + 0x1010) #define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL (HFI_COMMON_BASE + 0x1011) #define HFI_ERR_SESSION_INVALID_SCALE_FACTOR (HFI_COMMON_BASE + 0x1012) +#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED (HFI_COMMON_BASE + 0x1013) #define HFI_EVENT_SYS_ERROR (HFI_COMMON_BASE + 0x1) #define HFI_EVENT_SESSION_ERROR (HFI_COMMON_BASE + 0x2) From be23517b3cec05642f7db111fc9c8fce8a177a93 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Wed, 4 Jun 2014 17:20:13 +0530 Subject: [PATCH 020/104] msm: vidc: flush queued work during device suspend This change flushes any queued work related to power collapse, once the device is power suspended. There are applications which do not destroy video instance on suspending the device. For such applications, if the device is power suspended, video driver does not prepare venus for power collapse. Considerable power saving is observed by power collapsing venus before the device goes in suspend state. Change-Id: I11252e45b10d0d3d3eefb38994acd083c847bbb8 Signed-off-by: Vikash Garodia --- .../media/platform/msm/vidc/msm_v4l2_vidc.c | 30 +++++++++++++++++++ drivers/media/platform/msm/vidc/msm_vidc.c | 5 ++++ .../media/platform/msm/vidc/msm_vidc_common.c | 27 +++++++++++++++++ .../media/platform/msm/vidc/msm_vidc_common.h | 1 + drivers/media/platform/msm/vidc/venus_hfi.c | 20 +++++++++++++ .../media/platform/msm/vidc/vidc_hfi_api.h | 1 + include/media/msm_vidc.h | 1 + 7 files changed, 85 insertions(+) diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index 176c612b4294..88b8b68a7675 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -544,6 +544,35 @@ static const struct of_device_id msm_vidc_dt_match[] = { {} }; +static int msm_vidc_pm_suspend(struct device *pdev) +{ + struct msm_vidc_core *core; + + if (!pdev) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + + core = (struct msm_vidc_core *)pdev->platform_data; + if (!core) { + dprintk(VIDC_ERR, "%s invalid core\n", __func__); + return -EINVAL; + } + dprintk(VIDC_INFO, "%s\n", __func__); + + return msm_vidc_suspend(core->id); +} + +static int msm_vidc_pm_resume(struct device *dev) +{ + dprintk(VIDC_INFO, "%s\n", __func__); + return 0; +} + +static const struct dev_pm_ops msm_vidc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msm_vidc_pm_suspend, msm_vidc_pm_resume) +}; + MODULE_DEVICE_TABLE(of, msm_vidc_dt_match); static struct platform_driver msm_vidc_driver = { @@ -553,6 +582,7 @@ static struct platform_driver msm_vidc_driver = { .name = "msm_vidc_v4l2", .owner = THIS_MODULE, .of_match_table = msm_vidc_dt_match, + .pm = &msm_vidc_pm_ops, }, }; diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index fe0a42dc0bd9..92c669df307b 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -1397,3 +1397,8 @@ int msm_vidc_close(void *instance) kfree(inst); return 0; } + +int msm_vidc_suspend(int core_id) +{ + return msm_comm_suspend(core_id); +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 9fea431b8f12..72331d5f2e7c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -1995,6 +1995,33 @@ static int msm_comm_session_close(int flipped_state, return rc; } +int msm_comm_suspend(int core_id) +{ + struct hfi_device *hdev; + struct msm_vidc_core *core; + int rc = 0; + + core = get_vidc_core(core_id); + if (!core) { + dprintk(VIDC_ERR, + "%s: Failed to find core for core_id = %d\n", + __func__, core_id); + return -EINVAL; + } + + hdev = (struct hfi_device *)core->device; + if (!hdev) { + dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__); + return -EINVAL; + } + + rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data); + if (rc) + dprintk(VIDC_WARN, "Failed to suspend\n"); + + return rc; +} + static int get_flipped_state(int present_state, int desired_state) { diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h index e2f7b613d5ed..449832dda775 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -38,6 +38,7 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags); int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst); int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst); int msm_comm_force_cleanup(struct msm_vidc_inst *inst); +int msm_comm_suspend(int core_id); enum hal_extradata_id msm_comm_get_hal_extradata_index( enum v4l2_mpeg_vidc_extradata index); int msm_comm_get_domain_partition(struct msm_vidc_inst *inst, u32 flags, diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index c5cbfa388f5d..d7f80756abde 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -3291,6 +3291,25 @@ static int protect_cp_mem(struct venus_hfi_device *device) return rc; } +static int venus_hfi_suspend(void *dev) +{ + int rc = 0; + struct venus_hfi_device *device = (struct venus_hfi_device *) dev; + + if (!device) { + dprintk(VIDC_ERR, "%s invalid device\n", __func__); + return -EINVAL; + } + dprintk(VIDC_INFO, "%s\n", __func__); + + if (device->power_enabled) { + venus_hfi_try_clk_gating(device); + rc = flush_delayed_work(&venus_hfi_pm_work); + dprintk(VIDC_INFO, "%s flush delayed work %d\n", __func__, rc); + } + return 0; +} + static int venus_hfi_load_fw(void *dev) { int rc = 0; @@ -3687,6 +3706,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->capability_check = venus_hfi_capability_check; hdev->get_core_capabilities = venus_hfi_get_core_capabilities; hdev->power_enable = venus_hfi_power_enable; + hdev->suspend = venus_hfi_suspend; } int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id, diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 35beeec097ae..927d7046ab83 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -1172,6 +1172,7 @@ struct hfi_device { int (*session_clean)(void *sess); int (*get_core_capabilities)(void); int (*power_enable)(void *dev); + int (*suspend)(void *dev); }; typedef void (*hfi_cmd_response_callback) (enum command_response cmd, diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h index bbde6ef259b0..e48c7dac1021 100644 --- a/include/media/msm_vidc.h +++ b/include/media/msm_vidc.h @@ -55,6 +55,7 @@ enum smem_cache_ops { void *msm_vidc_open(int core_id, int session_type); int msm_vidc_close(void *instance); +int msm_vidc_suspend(int core_id); int msm_vidc_querycap(void *instance, struct v4l2_capability *cap); int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f); int msm_vidc_s_fmt(void *instance, struct v4l2_format *f); From 26c61481da345ce9611a449422a90ae54504594b Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 19 Nov 2014 20:49:01 +0300 Subject: [PATCH 021/104] VIDEO_MAX_FRAME --- drivers/media/platform/msm/vidc/msm_vdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index be11443ca9db..ae35d266b976 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -21,7 +21,7 @@ #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 -#define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME +#define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME #define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010 #define MB_SIZE_IN_PIXEL (16 * 16) From 5fc6c71437197b6dfd777025a81b28b6f6745736 Mon Sep 17 00:00:00 2001 From: Vikash Garodia Date: Fri, 21 Mar 2014 10:46:40 +0530 Subject: [PATCH 022/104] msm: vidc: queue output-1 buffers to FW While seeking, FW returns all the buffers to host. In multi-stream usecase like downscaling, the o/p buffers need to be re-queued back to FW. Change-Id: I5ea8534c06b3e48dadd4d73ab4432a13687a99c1 CRs-Fixed: 631827 Signed-off-by: Vikash Garodia Conflicts: drivers/media/platform/msm/vidc/msm_vdec.c --- drivers/media/platform/msm/vidc/msm_vdec.c | 60 +------------------ .../media/platform/msm/vidc/msm_vidc_common.c | 59 +++++++++++++++++- .../media/platform/msm/vidc/msm_vidc_common.h | 1 + 3 files changed, 59 insertions(+), 61 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index ae35d266b976..4b84d908f201 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -1219,64 +1219,6 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, return rc; } -static int msm_vdec_queue_output_buffers(struct msm_vidc_inst *inst) -{ - struct internal_buf *binfo; - struct hfi_device *hdev; - struct msm_smem *handle; - struct vidc_frame_data frame_data = {0}; - struct hal_buffer_requirements *output_buf, *extradata_buf; - int rc = 0; - hdev = inst->core->device; - - output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); - if (!output_buf) { - dprintk(VIDC_DBG, - "This output buffer not required, buffer_type: %x\n", - HAL_BUFFER_OUTPUT); - return 0; - } - dprintk(VIDC_DBG, - "output: num = %d, size = %d\n", - output_buf->buffer_count_actual, - output_buf->buffer_size); - - extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT); - if (!extradata_buf) { - dprintk(VIDC_DBG, - "This extradata buffer not required, buffer_type: %x\n", - HAL_BUFFER_EXTRADATA_OUTPUT); - return 0; - } - - hdev = inst->core->device; - - mutex_lock(&inst->lock); - if (!list_empty(&inst->outputbufs)) { - list_for_each_entry(binfo, &inst->outputbufs, list) { - if (!binfo) { - dprintk(VIDC_ERR, "Invalid parameter\n"); - mutex_unlock(&inst->lock); - return -EINVAL; - } - handle = binfo->handle; - frame_data.alloc_len = output_buf->buffer_size; - frame_data.filled_len = 0; - frame_data.offset = 0; - frame_data.device_addr = handle->device_addr; - frame_data.flags = 0; - frame_data.extradata_addr = handle->device_addr + - output_buf->buffer_size; - frame_data.buffer_type = HAL_BUFFER_OUTPUT; - rc = call_hfi_op(hdev, session_ftb, - (void *) inst->session, &frame_data); - binfo->buffer_ownership = FIRMWARE; - } - } - mutex_unlock(&inst->lock); - return 0; -} - static inline int start_streaming(struct msm_vidc_inst *inst) { int rc = 0; @@ -1327,7 +1269,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst) } if (msm_comm_get_stream_output_mode(inst) == HAL_VIDEO_DECODER_SECONDARY) { - rc = msm_vdec_queue_output_buffers(inst); + rc = msm_comm_queue_output_buffers(inst); if (rc) { dprintk(VIDC_ERR, "Failed to queue output buffers: %d\n", rc); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 72331d5f2e7c..bd7ae46c225d 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -732,8 +732,8 @@ void validate_output_buffers(struct msm_vidc_inst *inst) return; } if (binfo->buffer_ownership != DRIVER) { - dprintk(VIDC_ERR, - "Failed : This buffer is with FW 0x%lx\n", + dprintk(VIDC_DBG, + "This buffer is with FW 0x%lx\n", binfo->handle->device_addr); return; } @@ -747,16 +747,71 @@ void validate_output_buffers(struct msm_vidc_inst *inst) return; } + +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst) +{ + struct internal_buf *binfo; + struct hfi_device *hdev; + struct msm_smem *handle; + struct vidc_frame_data frame_data = {0}; + struct hal_buffer_requirements *output_buf; + int rc = 0; + + if (!inst || !inst->core || !inst->core->device) { + dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); + return -EINVAL; + } + hdev = inst->core->device; + output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT); + if (!output_buf) { + dprintk(VIDC_DBG, + "This output buffer not required, buffer_type: %x\n", + HAL_BUFFER_OUTPUT); + return 0; + } + dprintk(VIDC_DBG, + "output: num = %d, size = %d\n", + output_buf->buffer_count_actual, + output_buf->buffer_size); + + list_for_each_entry(binfo, &inst->outputbufs, list) { + if (binfo->buffer_ownership != DRIVER) + continue; + handle = binfo->handle; + frame_data.alloc_len = output_buf->buffer_size; + frame_data.filled_len = 0; + frame_data.offset = 0; + frame_data.device_addr = handle->device_addr; + frame_data.flags = 0; + frame_data.extradata_addr = handle->device_addr + + output_buf->buffer_size; + frame_data.buffer_type = HAL_BUFFER_OUTPUT; + rc = call_hfi_op(hdev, session_ftb, + (void *) inst->session, &frame_data); + binfo->buffer_ownership = FIRMWARE; + } + return 0; +} + static void handle_session_flush(enum command_response cmd, void *data) { struct msm_vidc_cb_cmd_done *response = data; struct msm_vidc_inst *inst; + int rc; if (response) { inst = (struct msm_vidc_inst *)response->session_id; if (msm_comm_get_stream_output_mode(inst) == HAL_VIDEO_DECODER_SECONDARY) { mutex_lock(&inst->lock); validate_output_buffers(inst); + if (!inst->in_reconfig) { + rc = msm_comm_queue_output_buffers(inst); + if (rc) { + dprintk(VIDC_ERR, + "Failed to queue output buffers: %d\n", + rc); + } + } mutex_unlock(&inst->lock); } msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE); diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h index 449832dda775..d3da22417e94 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -32,6 +32,7 @@ int msm_comm_try_set_prop(struct msm_vidc_inst *inst, int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst); int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst); int msm_comm_set_output_buffers(struct msm_vidc_inst *inst); +int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst); int msm_comm_qbuf(struct vb2_buffer *vb); void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst); int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags); From 41447e26c230aa88c07b8b63b3af6202da06727d Mon Sep 17 00:00:00 2001 From: Jorge Solano Altamirano Date: Wed, 29 Jan 2014 17:28:49 -0800 Subject: [PATCH 023/104] msm: vidc: add kzalloc success check for new q6 HAL session Memory allocation must be verified before proceeding. CRs-Fixed: 606522 Change-Id: I809acc21dcd5e680827feb5d0fa8f45814bf4773 Signed-off-by: Jorge Solano Altamirano --- drivers/media/platform/msm/vidc/q6_hfi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c index e70635d6275d..5404af627b5a 100644 --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -554,6 +554,10 @@ static void *q6_hfi_session_init(void *device, u32 session_id, new_session = (struct hal_session *) kzalloc(sizeof(struct hal_session), GFP_KERNEL); + if (!new_session) { + dprintk(VIDC_ERR, "new session fail: Out of memory\n"); + return NULL; + } new_session->session_id = (u32) session_id; if (session_type == 1) new_session->is_decoder = 0; From 27e349bcebe76e2f6856d3051c35ccc9ea0ca293 Mon Sep 17 00:00:00 2001 From: Prasad Nallani Date: Mon, 7 Apr 2014 11:14:52 +0530 Subject: [PATCH 024/104] msm: vidc: add input check for hfi q6 function. The input parameters may be null. This change adds input checks at function q6_hfi_iface_eventq_read CRs-Fixed: 606516 Change-Id: I801a9d3249e9b97fa218fc29bc3a2a0e7a0d4d3f Signed-off-by: Prasad Nallani --- drivers/media/platform/msm/vidc/q6_hfi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c index 5404af627b5a..486d740acbe5 100644 --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -146,8 +146,8 @@ static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt) struct q6_iface_q_info *q_info; unsigned long flags = 0; - if (!pkt) { - dprintk(VIDC_ERR, "Invalid Params"); + if (!device || !pkt) { + dprintk(VIDC_ERR, "Invalid Params\n"); return -EINVAL; } From a53841efa2ed4625e0682e308f7c4150236107ef Mon Sep 17 00:00:00 2001 From: Ravi Kiran Vonteddu Date: Fri, 23 May 2014 16:54:34 +0530 Subject: [PATCH 025/104] msm-vidc: Modify Q6 HFI not to alter VP8 max supported width Modify Q6 HFI not to alter the core returned max supported width and height. Whereas venus HFI will alter the the max supported width and height to 1080p. Change-Id: I901862e8fc98a1a6d80e62e7ab2d6477de5f07a4 Signed-off-by: Ravi Kiran Vonteddu Conflicts: drivers/media/platform/msm/vidc/msm_vidc_common.c drivers/media/platform/msm/vidc/venus_hfi.c drivers/media/platform/msm/vidc/vidc_hfi_api.h --- .../media/platform/msm/vidc/msm_vidc_common.c | 10 +++++----- drivers/media/platform/msm/vidc/q6_hfi.c | 19 +++++++++++++++++++ drivers/media/platform/msm/vidc/venus_hfi.c | 8 +++++--- .../media/platform/msm/vidc/vidc_hfi_api.h | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index bd7ae46c225d..a5b612e5aacb 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3443,14 +3443,14 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) capability->height.min); rc = -ENOTSUPP; } + if (!rc) { rc = call_hfi_op(hdev, capability_check, - inst->fmts[OUTPUT_PORT]->fourcc, - inst->prop.width[CAPTURE_PORT], - &capability->width.max, - &capability->height.max); + inst->fmts[OUTPUT_PORT]->fourcc, + inst->prop.width[CAPTURE_PORT], + &capability->width.max, + &capability->height.max); } - if (!rc && (inst->prop.height[CAPTURE_PORT] * inst->prop.width[CAPTURE_PORT] > capability->width.max * capability->height.max)) { diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c index 486d740acbe5..bbba29a9c7cc 100644 --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -1318,6 +1318,24 @@ static int q6_hfi_load_fw(void *dev) return rc; } +int q6_hfi_capability_check(u32 fourcc, u32 width, + u32 *max_width, u32 *max_height) +{ + int rc = 0; + if (!max_width || !max_height) { + dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__); + return -EINVAL; + } + + if (width > *max_width) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u\n", + width, *max_width); + rc = -ENOTSUPP; + } + return rc; +} + static void q6_hfi_unload_fw(void *hfi_device_data) { struct q6_hfi_device *device = hfi_device_data; @@ -1372,6 +1390,7 @@ static void q6_init_hfi_callbacks(struct hfi_device *hdev) hdev->unset_ocmem = q6_hfi_unset_ocmem; hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition; hdev->load_fw = q6_hfi_load_fw; + hdev->capability_check = q6_hfi_capability_check; hdev->unload_fw = q6_hfi_unload_fw; hdev->get_stride_scanline = q6_hfi_get_stride_scanline; } diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index d7f80756abde..ef537da0b9c0 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -3527,7 +3527,7 @@ int venus_hfi_get_core_capabilities(void) } int venus_hfi_capability_check(u32 fourcc, u32 width, - u32 *max_width, u32 *max_height) + u32 *max_width, u32 *max_height) { int rc = 0; if (!max_width || !max_height) { @@ -3539,10 +3539,11 @@ int venus_hfi_capability_check(u32 fourcc, u32 width, *max_width = DEFAULT_WIDTH; *max_height = DEFAULT_HEIGHT; } + if (width > *max_width) { dprintk(VIDC_ERR, - "Unsupported width = %u supported max width = %u", - width, *max_width); + "Unsupported width = %u supported max width = %u\n", + width, *max_width); rc = -ENOTSUPP; } return rc; @@ -3705,6 +3706,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->get_stride_scanline = venus_hfi_get_stride_scanline; hdev->capability_check = venus_hfi_capability_check; hdev->get_core_capabilities = venus_hfi_get_core_capabilities; + hdev->capability_check = venus_hfi_capability_check; hdev->power_enable = venus_hfi_power_enable; hdev->suspend = venus_hfi_suspend; } diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 927d7046ab83..8884c454a03d 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -1168,7 +1168,7 @@ struct hfi_device { int (*get_stride_scanline)(int color_fmt, int width, int height, int *stride, int *scanlines); int (*capability_check)(u32 fourcc, u32 width, - u32 *max_width, u32 *max_height); + u32 *max_width, u32 *max_height); int (*session_clean)(void *sess); int (*get_core_capabilities)(void); int (*power_enable)(void *dev); From d9a41400e3f4b22654bcc3395d9463bd0b887e17 Mon Sep 17 00:00:00 2001 From: Manikanta Sivapala Date: Tue, 10 Jun 2014 17:46:13 +0530 Subject: [PATCH 026/104] msm-vidc: Modify HFI not to alter VP8 max supported width Modify HFI not to alter firmware returned max supported width and height and make generic both for Venus and Q6. Remove creation of debugfs file for VP8 mode. Change-Id: I77d9077a79214d90f59eb7314bb66e310b5a418f Signed-off-by: Manikanta Sivapala --- .../media/platform/msm/vidc/msm_vidc_common.c | 14 +++++------ .../media/platform/msm/vidc/msm_vidc_debug.c | 6 ----- drivers/media/platform/msm/vidc/q6_hfi.c | 19 --------------- drivers/media/platform/msm/vidc/venus_hfi.c | 24 ------------------- .../media/platform/msm/vidc/vidc_hfi_api.h | 2 -- 5 files changed, 7 insertions(+), 58 deletions(-) mode change 100644 => 100755 drivers/media/platform/msm/vidc/msm_vidc_common.c mode change 100644 => 100755 drivers/media/platform/msm/vidc/msm_vidc_debug.c mode change 100644 => 100755 drivers/media/platform/msm/vidc/q6_hfi.c mode change 100644 => 100755 drivers/media/platform/msm/vidc/venus_hfi.c mode change 100644 => 100755 drivers/media/platform/msm/vidc/vidc_hfi_api.h diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c old mode 100644 new mode 100755 index a5b612e5aacb..0e10f159a0a2 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3443,13 +3443,13 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) capability->height.min); rc = -ENOTSUPP; } - - if (!rc) { - rc = call_hfi_op(hdev, capability_check, - inst->fmts[OUTPUT_PORT]->fourcc, - inst->prop.width[CAPTURE_PORT], - &capability->width.max, - &capability->height.max); + if (!rc && (inst->prop.width[CAPTURE_PORT] > + capability->width.max)) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u", + inst->prop.width[CAPTURE_PORT], + capability->width.max); + rc = -ENOTSUPP; } if (!rc && (inst->prop.height[CAPTURE_PORT] * inst->prop.width[CAPTURE_PORT] > diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c old mode 100644 new mode 100755 index 5a1826554d48..f272a3ecf7ac --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -20,7 +20,6 @@ int msm_vidc_debug_out = VIDC_OUT_PRINTK; int msm_fw_debug = 0x18; int msm_fw_debug_mode = 0x1; int msm_fw_low_power_mode = 0x1; -int msm_vp8_low_tier = 0x1; int msm_vidc_hw_rsp_timeout = 1000; struct debug_buffer { @@ -184,11 +183,6 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); goto failed_create_dir; } - if (!debugfs_create_u32("vp8_low_tier", S_IRUGO | S_IWUSR, - parent, &msm_vp8_low_tier)) { - dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); - goto failed_create_dir; - } if (!debugfs_create_u32("debug_output", S_IRUGO | S_IWUSR, parent, &msm_vidc_debug_out)) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c old mode 100644 new mode 100755 index bbba29a9c7cc..486d740acbe5 --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -1318,24 +1318,6 @@ static int q6_hfi_load_fw(void *dev) return rc; } -int q6_hfi_capability_check(u32 fourcc, u32 width, - u32 *max_width, u32 *max_height) -{ - int rc = 0; - if (!max_width || !max_height) { - dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__); - return -EINVAL; - } - - if (width > *max_width) { - dprintk(VIDC_ERR, - "Unsupported width = %u supported max width = %u\n", - width, *max_width); - rc = -ENOTSUPP; - } - return rc; -} - static void q6_hfi_unload_fw(void *hfi_device_data) { struct q6_hfi_device *device = hfi_device_data; @@ -1390,7 +1372,6 @@ static void q6_init_hfi_callbacks(struct hfi_device *hdev) hdev->unset_ocmem = q6_hfi_unset_ocmem; hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition; hdev->load_fw = q6_hfi_load_fw; - hdev->capability_check = q6_hfi_capability_check; hdev->unload_fw = q6_hfi_unload_fw; hdev->get_stride_scanline = q6_hfi_get_stride_scanline; } diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c old mode 100644 new mode 100755 index ef537da0b9c0..3fc4d13d5b26 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -3526,29 +3526,6 @@ int venus_hfi_get_core_capabilities(void) return rc; } -int venus_hfi_capability_check(u32 fourcc, u32 width, - u32 *max_width, u32 *max_height) -{ - int rc = 0; - if (!max_width || !max_height) { - dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__); - return -EINVAL; - } - - if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) { - *max_width = DEFAULT_WIDTH; - *max_height = DEFAULT_HEIGHT; - } - - if (width > *max_width) { - dprintk(VIDC_ERR, - "Unsupported width = %u supported max width = %u\n", - width, *max_width); - rc = -ENOTSUPP; - } - return rc; -} - static void *venus_hfi_add_device(u32 device_id, struct msm_vidc_platform_resources *res, hfi_cmd_response_callback callback) @@ -3706,7 +3683,6 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->get_stride_scanline = venus_hfi_get_stride_scanline; hdev->capability_check = venus_hfi_capability_check; hdev->get_core_capabilities = venus_hfi_get_core_capabilities; - hdev->capability_check = venus_hfi_capability_check; hdev->power_enable = venus_hfi_power_enable; hdev->suspend = venus_hfi_suspend; } diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h old mode 100644 new mode 100755 index 8884c454a03d..9a30dec1ca65 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -1167,8 +1167,6 @@ struct hfi_device { int (*get_info) (void *dev, enum dev_info info); int (*get_stride_scanline)(int color_fmt, int width, int height, int *stride, int *scanlines); - int (*capability_check)(u32 fourcc, u32 width, - u32 *max_width, u32 *max_height); int (*session_clean)(void *sess); int (*get_core_capabilities)(void); int (*power_enable)(void *dev); From be2865ab9f1e6d9c3beffe9f28974373da53fdb5 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 19 Nov 2014 21:30:33 +0300 Subject: [PATCH 027/104] Revert "msm-vidc: Modify HFI not to alter VP8 max supported width" This reverts commit d9a41400e3f4b22654bcc3395d9463bd0b887e17. --- .../media/platform/msm/vidc/msm_vidc_common.c | 14 +++++------ .../media/platform/msm/vidc/msm_vidc_debug.c | 6 +++++ drivers/media/platform/msm/vidc/q6_hfi.c | 19 +++++++++++++++ drivers/media/platform/msm/vidc/venus_hfi.c | 24 +++++++++++++++++++ .../media/platform/msm/vidc/vidc_hfi_api.h | 2 ++ 5 files changed, 58 insertions(+), 7 deletions(-) mode change 100755 => 100644 drivers/media/platform/msm/vidc/msm_vidc_common.c mode change 100755 => 100644 drivers/media/platform/msm/vidc/msm_vidc_debug.c mode change 100755 => 100644 drivers/media/platform/msm/vidc/q6_hfi.c mode change 100755 => 100644 drivers/media/platform/msm/vidc/venus_hfi.c mode change 100755 => 100644 drivers/media/platform/msm/vidc/vidc_hfi_api.h diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c old mode 100755 new mode 100644 index 0e10f159a0a2..a5b612e5aacb --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -3443,13 +3443,13 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst) capability->height.min); rc = -ENOTSUPP; } - if (!rc && (inst->prop.width[CAPTURE_PORT] > - capability->width.max)) { - dprintk(VIDC_ERR, - "Unsupported width = %u supported max width = %u", - inst->prop.width[CAPTURE_PORT], - capability->width.max); - rc = -ENOTSUPP; + + if (!rc) { + rc = call_hfi_op(hdev, capability_check, + inst->fmts[OUTPUT_PORT]->fourcc, + inst->prop.width[CAPTURE_PORT], + &capability->width.max, + &capability->height.max); } if (!rc && (inst->prop.height[CAPTURE_PORT] * inst->prop.width[CAPTURE_PORT] > diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c old mode 100755 new mode 100644 index f272a3ecf7ac..5a1826554d48 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -20,6 +20,7 @@ int msm_vidc_debug_out = VIDC_OUT_PRINTK; int msm_fw_debug = 0x18; int msm_fw_debug_mode = 0x1; int msm_fw_low_power_mode = 0x1; +int msm_vp8_low_tier = 0x1; int msm_vidc_hw_rsp_timeout = 1000; struct debug_buffer { @@ -183,6 +184,11 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); goto failed_create_dir; } + if (!debugfs_create_u32("vp8_low_tier", S_IRUGO | S_IWUSR, + parent, &msm_vp8_low_tier)) { + dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); + goto failed_create_dir; + } if (!debugfs_create_u32("debug_output", S_IRUGO | S_IWUSR, parent, &msm_vidc_debug_out)) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c old mode 100755 new mode 100644 index 486d740acbe5..bbba29a9c7cc --- a/drivers/media/platform/msm/vidc/q6_hfi.c +++ b/drivers/media/platform/msm/vidc/q6_hfi.c @@ -1318,6 +1318,24 @@ static int q6_hfi_load_fw(void *dev) return rc; } +int q6_hfi_capability_check(u32 fourcc, u32 width, + u32 *max_width, u32 *max_height) +{ + int rc = 0; + if (!max_width || !max_height) { + dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__); + return -EINVAL; + } + + if (width > *max_width) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u\n", + width, *max_width); + rc = -ENOTSUPP; + } + return rc; +} + static void q6_hfi_unload_fw(void *hfi_device_data) { struct q6_hfi_device *device = hfi_device_data; @@ -1372,6 +1390,7 @@ static void q6_init_hfi_callbacks(struct hfi_device *hdev) hdev->unset_ocmem = q6_hfi_unset_ocmem; hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition; hdev->load_fw = q6_hfi_load_fw; + hdev->capability_check = q6_hfi_capability_check; hdev->unload_fw = q6_hfi_unload_fw; hdev->get_stride_scanline = q6_hfi_get_stride_scanline; } diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c old mode 100755 new mode 100644 index 3fc4d13d5b26..ef537da0b9c0 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -3526,6 +3526,29 @@ int venus_hfi_get_core_capabilities(void) return rc; } +int venus_hfi_capability_check(u32 fourcc, u32 width, + u32 *max_width, u32 *max_height) +{ + int rc = 0; + if (!max_width || !max_height) { + dprintk(VIDC_ERR, "%s - invalid parameter\n", __func__); + return -EINVAL; + } + + if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) { + *max_width = DEFAULT_WIDTH; + *max_height = DEFAULT_HEIGHT; + } + + if (width > *max_width) { + dprintk(VIDC_ERR, + "Unsupported width = %u supported max width = %u\n", + width, *max_width); + rc = -ENOTSUPP; + } + return rc; +} + static void *venus_hfi_add_device(u32 device_id, struct msm_vidc_platform_resources *res, hfi_cmd_response_callback callback) @@ -3683,6 +3706,7 @@ static void venus_init_hfi_callbacks(struct hfi_device *hdev) hdev->get_stride_scanline = venus_hfi_get_stride_scanline; hdev->capability_check = venus_hfi_capability_check; hdev->get_core_capabilities = venus_hfi_get_core_capabilities; + hdev->capability_check = venus_hfi_capability_check; hdev->power_enable = venus_hfi_power_enable; hdev->suspend = venus_hfi_suspend; } diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h old mode 100755 new mode 100644 index 9a30dec1ca65..8884c454a03d --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -1167,6 +1167,8 @@ struct hfi_device { int (*get_info) (void *dev, enum dev_info info); int (*get_stride_scanline)(int color_fmt, int width, int height, int *stride, int *scanlines); + int (*capability_check)(u32 fourcc, u32 width, + u32 *max_width, u32 *max_height); int (*session_clean)(void *sess); int (*get_core_capabilities)(void); int (*power_enable)(void *dev); From 3a143890e636a5356398c21faf7e87b54eaab3ab Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 18:50:29 +0300 Subject: [PATCH 028/104] Revert "msm: camera: Return current lens position in actuator driver" This reverts commit a66ddbd14d999d4ee7aaa545a269fee36cc3dc3e. --- .../platform/msm/camera_v2/sensor/actuator/msm_actuator.c | 5 +---- include/media/msm_cam_sensor.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 4a2f8746486c..292ff07fd48f 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -291,9 +291,6 @@ static int32_t msm_actuator_move_focus( /* LGE_CHANGE_E, fix kernel sometimes crash while AF, 2013.11.4, yousung.kang@lge.com */ #endif - curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; - move_params->curr_lens_pos = curr_lens_pos; - if (copy_from_user(&ringing_params_kernel, &(move_params->ringing_params[a_ctrl->curr_region_index]), sizeof(struct damping_params_t))) { @@ -306,6 +303,7 @@ static int32_t msm_actuator_move_focus( if (dest_step_pos == a_ctrl->curr_step_pos) return rc; + curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos]; a_ctrl->i2c_tbl_index = 0; CDBG("curr_step_pos =%d dest_step_pos =%d curr_lens_pos=%d\n", a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); @@ -344,7 +342,6 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos = target_step_pos; } - move_params->curr_lens_pos = curr_lens_pos; reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; reg_setting.data_type = a_ctrl->i2c_data_type; reg_setting.size = a_ctrl->i2c_tbl_index; diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 1b52914d922e..4c865363a77b 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -504,7 +504,6 @@ struct msm_actuator_move_params_t { int8_t sign_dir; int16_t dest_step_pos; int32_t num_steps; - uint16_t curr_lens_pos; struct damping_params_t *ringing_params; }; From 7fe61b5c544eee2cad00370767732ef37d45f7e2 Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 18:50:50 +0300 Subject: [PATCH 029/104] Revert "msm: camera2: add api for set actuator for manual af" This reverts commit fa1b13fa74159804c2219cef7f32e64fd57b4bb6. --- .../camera_v2/sensor/actuator/msm_actuator.c | 44 ------------------- .../camera_v2/sensor/actuator/msm_actuator.h | 2 - include/media/msm_cam_sensor.h | 10 ----- 3 files changed, 56 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 292ff07fd48f..706dbabd16c0 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -496,43 +496,6 @@ static int32_t msm_actuator_power_down(struct msm_actuator_ctrl_t *a_ctrl) return rc; } -static int32_t msm_actuator_set_position( - struct msm_actuator_ctrl_t *a_ctrl, - struct msm_actuator_set_position_t *set_pos) -{ - int32_t rc = 0; - int32_t index; - uint16_t next_lens_position; - uint16_t delay; - uint32_t hw_params = 0; - struct msm_camera_i2c_reg_setting reg_setting; - CDBG("%s Enter %d\n", __func__, __LINE__); - if (set_pos->number_of_steps == 0) - return rc; - - a_ctrl->i2c_tbl_index = 0; - for (index = 0; index < set_pos->number_of_steps; index++) { - next_lens_position = set_pos->pos[index]; - delay = set_pos->delay[index]; - a_ctrl->func_tbl->actuator_parse_i2c_params(a_ctrl, - next_lens_position, hw_params, delay); - - reg_setting.reg_setting = a_ctrl->i2c_reg_tbl; - reg_setting.size = a_ctrl->i2c_tbl_index; - reg_setting.data_type = a_ctrl->i2c_data_type; - - rc = a_ctrl->i2c_client.i2c_func_tbl->i2c_write_table_w_microdelay( - &a_ctrl->i2c_client, ®_setting); - if (rc < 0) { - pr_err("%s Failed I2C write Line %d\n", __func__, __LINE__); - return rc; - } - a_ctrl->i2c_tbl_index = 0; - } - CDBG("%s exit %d\n", __func__, __LINE__); - return rc; -} - static int32_t msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl, struct msm_actuator_set_info_t *set_info) { struct reg_settings_t *init_settings = NULL; @@ -690,12 +653,6 @@ static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl, pr_err("move focus failed %d\n", rc); break; - case CFG_SET_POSITION: - rc = a_ctrl->func_tbl->actuator_set_position(a_ctrl, - &cdata->cfg.setpos); - if (rc < 0) - pr_err("actuator_set_position failed %d\n", rc); - break; default: break; } @@ -1197,7 +1154,6 @@ static struct msm_actuator msm_vcm_actuator_table = { .actuator_set_default_focus = msm_actuator_set_default_focus, .actuator_init_focus = msm_actuator_init_focus, .actuator_parse_i2c_params = msm_actuator_parse_i2c_params, - .actuator_set_position = msm_actuator_set_position, }, }; diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h index 42a141ab3a7e..3b29401b73d1 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.h @@ -48,8 +48,6 @@ struct msm_actuator_func_tbl { struct damping_params_t *, int8_t, int16_t); - int32_t (*actuator_set_position)(struct msm_actuator_ctrl_t *, - struct msm_actuator_set_position_t *); }; struct msm_actuator { diff --git a/include/media/msm_cam_sensor.h b/include/media/msm_cam_sensor.h index 4c865363a77b..239dea222918 100644 --- a/include/media/msm_cam_sensor.h +++ b/include/media/msm_cam_sensor.h @@ -47,7 +47,6 @@ #define MAX_EEPROM_NAME 32 #define MAX_AF_ITERATIONS 3 -#define MAX_NUMBER_OF_STEPS 47 enum flash_type { LED_FLASH = 1, @@ -461,7 +460,6 @@ enum msm_actuator_cfg_type_t { CFG_GET_ACTUATOR_INFO, CFG_SET_ACTUATOR_INFO, CFG_SET_DEFAULT_FOCUS, - CFG_SET_POSITION, CFG_MOVE_FOCUS, }; @@ -559,13 +557,6 @@ enum af_camera_name { ACTUATOR_WEB_CAM_2, }; - -struct msm_actuator_set_position_t { - uint16_t number_of_steps; - uint16_t pos[MAX_NUMBER_OF_STEPS]; - uint16_t delay[MAX_NUMBER_OF_STEPS]; -}; - struct msm_actuator_cfg_data { int cfgtype; uint8_t is_af_supported; @@ -573,7 +564,6 @@ struct msm_actuator_cfg_data { struct msm_actuator_move_params_t move; struct msm_actuator_set_info_t set_info; struct msm_actuator_get_info_t get_info; - struct msm_actuator_set_position_t setpos; enum af_camera_name cam_name; } cfg; }; From 3a8a7aeb13068e0d6d16b821fdbcfb461989787b Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 18:52:20 +0300 Subject: [PATCH 030/104] Revert "msm: camera: Cleanup msm_generic_buff list while close" This reverts commit 4f8515fdc9c60f6c42cc4b7e97187212b4548718. Conflicts: drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h --- drivers/media/platform/msm/camera_v2/msm.c | 11 +---------- .../msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c | 2 +- .../msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index 10f94c934398..b1a23b4713bb 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,8 +29,6 @@ #include "msm.h" #include "msm_vb2.h" #include "msm_sd.h" -#include - static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; @@ -506,7 +504,6 @@ static void msm_remove_session_cmd_ack_q(struct msm_session *session) int msm_destroy_session(unsigned int session_id) { struct msm_session *session; - struct v4l2_subdev *buf_mgr_subdev; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); @@ -518,12 +515,6 @@ int msm_destroy_session(unsigned int session_id) mutex_destroy(&session->lock); msm_delete_entry(msm_session_q, struct msm_session, list, session); - buf_mgr_subdev = msm_buf_mngr_get_subdev(); - if (buf_mgr_subdev) { - v4l2_subdev_call(buf_mgr_subdev, core, ioctl, - MSM_SD_SHUTDOWN, NULL); - } else - pr_err("%s: Buff manger device node is NULL\n", __func__); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 299de090b7be..996b6d0039da 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index b06d4da7a8fd..9a1105520240 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and From b3b158ccdeb0f155fca2dcf1b2c4e2d9c3af174d Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 18:52:42 +0300 Subject: [PATCH 031/104] Revert "msm-camera: Fixed MIPI raw snaspshot issue 8x10" This reverts commit b56c00f46468f0554d8bcddeb63b5ef41c8991d1. --- .../media/platform/msm/camera_v2/isp/msm_isp32.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index 3065f625ead2..e0c32417a889 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -154,13 +154,6 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); //Aravind msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18); //Aravind - - msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); - msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); - msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); - msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); - msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); - //Aravind start vfe_dev->error_info.error_mask1 =0; vfe_dev->error_info.error_mask0 =0; @@ -168,13 +161,8 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) vfe_dev->error_info.violation_status =0; //Aravind end #else - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); - msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); - msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); - msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); - msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); - msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); - msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); + msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); + msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); #endif } From bfc9fefe10d114559cc847e0a814352f7687ef2b Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 19:00:50 +0300 Subject: [PATCH 032/104] Revert "camera: add LGE_CHANGE" This reverts commit 28ffc13b6663a4cabe3f06ce8c0379ff532af54c. --- .../platform/msm/camera_v2/isp/msm_buf_mgr.c | 46 ------ .../platform/msm/camera_v2/isp/msm_isp_util.c | 67 --------- .../platform/msm/camera_v2/ispif/msm_ispif.c | 27 +--- .../msm_buf_mgr/msm_generic_buf_mgr.c | 4 - .../msm_buf_mgr/msm_generic_buf_mgr.h | 1 - .../msm/camera_v2/pproc/cpp/msm_cpp.c | 77 +--------- .../msm/camera_v2/sensor/cci/msm_cci.c | 15 +- .../msm/camera_v2/sensor/eeprom/msm_eeprom.c | 133 +----------------- 8 files changed, 12 insertions(+), 358 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 6c772eed33b0..a85f8536ce2d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -309,11 +309,6 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, struct msm_isp_buffer *temp_buf_info; struct msm_isp_bufq *bufq = NULL; struct vb2_buffer *vb2_buf = NULL; -/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ - struct buffer_cmd *buf_pending = NULL; - struct msm_isp_buffer_mapped_info *mped_info_tmp1; - struct msm_isp_buffer_mapped_info *mped_info_tmp2; -/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle); if (!bufq) { pr_err("%s: Invalid bufq\n", __func__); @@ -353,30 +348,9 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, list_for_each_entry(temp_buf_info, &bufq->head, list) { if (temp_buf_info->state == MSM_ISP_BUFFER_STATE_QUEUED) { -/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ -#if 0 //QCT original /* found one buf */ list_del_init(&temp_buf_info->list); *buf_info = temp_buf_info; -#else - - list_for_each_entry(buf_pending, &buf_mgr->buffer_q, list) { - if (!buf_pending) - break; - mped_info_tmp1 = buf_pending->mapped_info; - mped_info_tmp2 = &temp_buf_info->mapped_info[0]; - - if (mped_info_tmp1 == mped_info_tmp2 - && (mped_info_tmp1->len == mped_info_tmp2->len) - && (mped_info_tmp1->paddr == mped_info_tmp2->paddr)) { - /* found one buf */ - list_del_init(&temp_buf_info->list); - *buf_info = temp_buf_info; - break; - } - } -#endif -/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ break; } } @@ -385,29 +359,9 @@ static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id, bufq->session_id, bufq->stream_id); if (vb2_buf) { if (vb2_buf->v4l2_buf.index < bufq->num_bufs) { -/* LGE_CHANGE_S, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [STARTS HERE] */ -#if 0 //QCT Original *buf_info = &bufq->bufs[vb2_buf->v4l2_buf.index]; (*buf_info)->vb2_buf = vb2_buf; -#else - list_for_each_entry(buf_pending, &buf_mgr->buffer_q, list) { - if (!buf_pending) - break; - mped_info_tmp1 = buf_pending->mapped_info; - mped_info_tmp2 = - &bufq->bufs[vb2_buf->v4l2_buf.index].mapped_info[0]; - - if (mped_info_tmp1 == mped_info_tmp2 - && (mped_info_tmp1->len == mped_info_tmp2->len) - && (mped_info_tmp1->paddr == mped_info_tmp2->paddr)) { - *buf_info = &bufq->bufs[vb2_buf->v4l2_buf.index]; - (*buf_info)->vb2_buf = vb2_buf; - break; - } - } -#endif -/* LGE_CHANGE_E, jaehan.jeong, 2014.1.15, check buffer validity in isp buf mgr, CN#01398582, [ENDS HERE] */ } else { pr_err("%s: Incorrect buf index %d\n", __func__, vb2_buf->v4l2_buf.index); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index ee1876fdd33a..734725101377 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -544,8 +544,6 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, reg_cfg_cmd->u.mask_info.reg_offset); break; } -/* LGE_CHANGE_S, jaehan.jeong, 2013.11.8, Applied QCT patch CN#01252253 - page fault in case of 64BIT_DMI, [STARTS HERE] */ -#if 0 //QCT Original case VFE_WRITE_DMI_16BIT: case VFE_WRITE_DMI_32BIT: case VFE_WRITE_DMI_64BIT: { @@ -594,71 +592,6 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, } break; } -#else - case VFE_WRITE_DMI_16BIT: - case VFE_WRITE_DMI_32BIT: { - int i; - uint32_t *lo_tbl_ptr = NULL; - uint32_t lo_val, lo_val1; - - if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset + - reg_cfg_cmd->u.dmi_info.len > cmd_len) { - pr_err("Invalid Lo Table out of bounds\n"); - return -EINVAL; - } - lo_tbl_ptr = cfg_data + - reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; - - for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) { - lo_val = *lo_tbl_ptr++; - if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) { - lo_val1 = lo_val & 0x0000FFFF; - lo_val = (lo_val & 0xFFFF0000)>>16; - msm_camera_io_w(lo_val1, vfe_dev->vfe_base + - vfe_dev->hw_info->dmi_reg_offset + 0x4); - } - msm_camera_io_w(lo_val, vfe_dev->vfe_base + - vfe_dev->hw_info->dmi_reg_offset + 0x4); - } - break; - } - case VFE_WRITE_DMI_64BIT: { - int i; - uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL; - uint32_t hi_val, lo_val; - - if (reg_cfg_cmd->u.dmi_info.hi_tbl_offset + - reg_cfg_cmd->u.dmi_info.len > cmd_len) { - pr_err("Invalid Hi Table out of bounds\n"); - return -EINVAL; - } - - if (reg_cfg_cmd->u.dmi_info.lo_tbl_offset + - reg_cfg_cmd->u.dmi_info.len > cmd_len) { - pr_err("Invalid Lo Table out of bounds\n"); - return -EINVAL; - } - - hi_tbl_ptr = cfg_data + - reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4; - - lo_tbl_ptr = cfg_data + - reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; - - for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/8; i++) { - lo_val = *lo_tbl_ptr; - hi_val = *hi_tbl_ptr; - lo_tbl_ptr = lo_tbl_ptr + 2; - hi_tbl_ptr = hi_tbl_ptr + 2; - msm_camera_io_w(hi_val, vfe_dev->vfe_base + - vfe_dev->hw_info->dmi_reg_offset); - msm_camera_io_w(lo_val, vfe_dev->vfe_base + - vfe_dev->hw_info->dmi_reg_offset + 0x4); - } - break; - } -#endif -/* LGE_CHANGE_E, jaehan.jeong, 2013.11.8, Applied QCT patch CN#01252253 - page fault in case of 64BIT_DMI, [ENDS HERE] */ case VFE_READ_DMI_16BIT: case VFE_READ_DMI_32BIT: case VFE_READ_DMI_64BIT: { diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index cdb4094a888c..9a8963df0b76 100755 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -97,19 +97,13 @@ static int msm_ispif_reset_hw(struct ispif_device *ispif) int rc = 0; long timeout = 0; struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)]; -#if defined(CONFIG_HI351) - if(ispif->hw_num_isps > 1){ -#else - { -#endif -/*LGE CHANGE_E, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ + rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_reset_clk_info, reset_clk, ARRAY_SIZE(ispif_8974_reset_clk_info), 1); - if (rc < 0) { - pr_err("%s: cannot enable clock, error = %d", - __func__, rc); - } + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); } init_completion(&ispif->reset_complete[VFE0]); @@ -166,13 +160,6 @@ static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable) /* Older ISPIF versiond don't need ahb clokc */ return 0; } -/*LGE CHANGE_S, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ -#if defined(CONFIG_HI351) - if(ispif->hw_num_isps > 1){ -#else - { -#endif -/*LGE CHANGE_E, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_ahb_clk_info, &ispif->ahb_clk, @@ -181,7 +168,6 @@ static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable) pr_err("%s: cannot enable clock, error = %d", __func__, rc); } - } return rc; } @@ -953,10 +939,7 @@ static int msm_ispif_init(struct ispif_device *ispif, pr_err("%s: ahb_clk enable failed", __func__); goto error_ahb; } -/*LGE CHANGE_S, 2013-12-13, this is for shutter lag issue, STOP_IMMEDIATELY, youngwook.song@lge.com */ -#if defined(CONFIG_HI351) - msm_ispif_reset_hw(ispif); -#endif + if (of_device_is_compatible(ispif->pdev->dev.of_node, "qcom,ispif-v3.0")) { /* currently HW reset is implemented for 8974 only */ diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c index 996b6d0039da..d1a6176a475d 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c @@ -129,7 +129,6 @@ static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } - buf_mngr_dev->msm_buf_mngr_open_cnt++; return rc; } @@ -143,9 +142,6 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, rc = -ENODEV; return rc; } - buf_mngr_dev->msm_buf_mngr_open_cnt--; - if (buf_mngr_dev->msm_buf_mngr_open_cnt == 0) - msm_buf_mngr_sd_shutdown(buf_mngr_dev); return rc; } diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h index 9a1105520240..56886cd94843 100644 --- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h +++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h @@ -36,6 +36,5 @@ struct msm_buf_mngr_device { spinlock_t buf_q_spinlock; struct msm_sd_subdev subdev; struct msm_sd_req_vb2_q vb2_ops; - uint32_t msm_buf_mngr_open_cnt; /* LGE_CHANGE, jaehan.jeong, 2013.12.29, QCT PATCH, Cleanup msm generic buf queue handling */ }; #endif diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index fa13b48b75e3..10a0085ed58a 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -94,23 +94,6 @@ static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, spin_unlock_irqrestore(&__q->lock, flags); \ qcmd; \ }) -/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ -#define msm_cpp_empty_list(queue, member) { \ - unsigned long flags; \ - struct msm_queue_cmd *qcmd = NULL; \ - if (queue) { \ - spin_lock_irqsave(&queue->lock, flags); \ - while (!list_empty(&queue->list)) { \ - queue->len--; \ - qcmd = list_first_entry(&queue->list, \ - struct msm_queue_cmd, member); \ - list_del_init(&qcmd->member); \ - kfree(qcmd); \ - } \ - spin_unlock_irqrestore(&queue->lock, flags); \ - } \ -} -/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ static void msm_queue_init(struct msm_device_queue *queue, const char *name) { @@ -158,20 +141,6 @@ static int msm_cpp_enable_debugfs(struct cpp_device *cpp_dev); static void msm_cpp_write(u32 data, void __iomem *cpp_base) { -/* LGE_CHANGE_S, QCT patch for CPP, 2013-11-19, hyungtae.lee@lge.com */ - uint32_t tmp; - int num_tries=50; - do { - tmp = msm_camera_io_r(cpp_base + MSM_CPP_MICRO_FIFO_RX_STAT); - num_tries --; - } while (((tmp & 0x1) == 0x0) && (num_tries > 0)); - - if(num_tries <= 0) { - pr_err("error: cant write, RX FIFO Full\n"); - return; - } -/* LGE_CHANGE_E, QCT patch for CPP, 2013-11-19, hyungtae.lee@lge.com */ - writel_relaxed((data), cpp_base + MSM_CPP_MICRO_FIFO_RX_DATA); } @@ -945,23 +914,8 @@ static int cpp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) uint32_t i; struct cpp_device *cpp_dev = v4l2_get_subdevdata(sd); -/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - struct msm_device_queue *processing_q = NULL; - struct msm_device_queue *eventData_q = NULL; - - if (!cpp_dev) { - pr_err("failed: cpp_dev %p\n", cpp_dev); - return -EINVAL; - } -/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - mutex_lock(&cpp_dev->mutex); -/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - processing_q = &cpp_dev->processing_q; - eventData_q = &cpp_dev->eventData_q; -/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - if (cpp_dev->cpp_open_cnt == 0) { mutex_unlock(&cpp_dev->mutex); return 0; @@ -1043,25 +997,13 @@ static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev, static int msm_cpp_notify_frame_done(struct cpp_device *cpp_dev) { struct v4l2_event v4l2_evt; -/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - struct msm_queue_cmd *frame_qcmd = NULL; - struct msm_queue_cmd *event_qcmd = NULL; - struct msm_cpp_frame_info_t *processed_frame = NULL; -/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - struct msm_device_queue *queue = &cpp_dev->processing_q; + struct msm_queue_cmd *frame_qcmd; + struct msm_queue_cmd *event_qcmd; + struct msm_cpp_frame_info_t *processed_frame; + struct msm_device_queue *queue = &cpp_dev->processing_q; struct msm_buf_mngr_info buff_mgr_info; int rc = 0; -/*QCT_PATCH S, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ -#if 0 - if (queue->len > 0) { - frame_qcmd = msm_dequeue(queue, list_frame); -#else - frame_qcmd = msm_dequeue(queue, list_frame); - if (frame_qcmd) { -#endif -/*QCT_PATCH E, add code to be freed event_qcmd when SHUTDOWN, 2013-12-12, yousung.kang@lge.com */ - if (queue->len > 0) { frame_qcmd = msm_dequeue(queue, list_frame); processed_frame = frame_qcmd->command; @@ -1353,16 +1295,6 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, new_frame->duplicate_identity); memset(&new_frame->output_buffer_info[1], 0, sizeof(struct msm_cpp_buffer_info_t)); -/* LGE_CHANGE_S, jaehan.jeong, 2013.12.29, Release vb2 buffer in cpp driver on error, [STARTS HERE] */ -#if 0 //QCT original - memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); - buff_mgr_info.session_id = - ((new_frame->duplicate_identity >> 16) & 0xFFFF); - buff_mgr_info.stream_id = - (new_frame->duplicate_identity & 0xFFFF); - rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, - &buff_mgr_info); -#else memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info)); dup_buff_mgr_info.session_id = ((new_frame->duplicate_identity >> 16) & 0xFFFF); @@ -1370,7 +1302,6 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev, (new_frame->duplicate_identity & 0xFFFF); rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF, &dup_buff_mgr_info); -#endif if (rc < 0) { rc = -EAGAIN; pr_debug("error getting buffer rc:%d\n", rc); diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index e68739e2669f..e072e53057b2 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -29,7 +29,7 @@ #define CYCLES_PER_MICRO_SEC 4915 #define CCI_MAX_DELAY 10000 -#define CCI_TIMEOUT msecs_to_jiffies(300) +#define CCI_TIMEOUT msecs_to_jiffies(100) /* TODO move this somewhere else */ #define MSM_CCI_DRV_NAME "msm_cci" @@ -778,7 +778,6 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, struct msm_camera_cci_ctrl *cci_ctrl) { int32_t rc = 0; - int32_t trialCnt = 3; /*QCT_PATCH, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ CDBG("%s line %d cmd %d\n", __func__, __LINE__, cci_ctrl->cmd); switch (cci_ctrl->cmd) { @@ -792,18 +791,6 @@ static int32_t msm_cci_config(struct v4l2_subdev *sd, rc = msm_cci_i2c_read_bytes(sd, cci_ctrl); break; case MSM_CCI_I2C_WRITE: -/*QCT_PATCH E, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ -#if 1 // QCT Test - do{ - rc = msm_cci_i2c_write(sd, cci_ctrl); - if(rc < 0) - pr_err("%s: line %d trialCnt = %d \n", __func__, __LINE__, trialCnt); - trialCnt--; - }while(rc < 0 && trialCnt > 0); -#else - rc = msm_cci_i2c_write(sd, cci_ctrl); -#endif -/*QCT_PATCH E, add the retrial code only in msm_cci_config() function , 2013-12-09, yousung.kang@lge.com */ rc = msm_cci_i2c_write(sd, cci_ctrl); break; case MSM_CCI_GPIO_WRITE: diff --git a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c index 845da3e69eb7..69c1faa5dd48 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c +++ b/drivers/media/platform/msm/camera_v2/sensor/eeprom/msm_eeprom.c @@ -24,9 +24,6 @@ #define CDBG(fmt, args...) pr_debug(fmt, ##args) #endif -// [LGE_UPDATE] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) -char *lge_camera_info = NULL; - DEFINE_MSM_MUTEX(msm_eeprom_mutex); int32_t msm_eeprom_config(struct msm_eeprom_ctrl_t *e_ctrl, @@ -219,42 +216,6 @@ int32_t read_eeprom_memory(struct msm_eeprom_ctrl_t *e_ctrl) return rc; } memptr += emap[j].mem.valid_size; -// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up - /* - *We need to change slave address for read data (Y412B) - *I2C addr : 0x50, 0x51, 0x52, 0x53 - */ - #if !defined(CONFIG_ARCH_MSM8610) - #if defined(CONFIG_IMX111) - CDBG("%s: I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.cci_client->sid); - e_ctrl->i2c_client.cci_client->sid++; - #endif - #else - #if defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) - if(lge_camera_info){ - if(!strncmp(lge_camera_info, "imx111", 6)){ - CDBG("%s: (global)I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.client->addr); - e_ctrl->i2c_client.client->addr+=0x2; - } - } - #else - #if defined(CONFIG_IMX111) - CDBG("%s: I2C address : 0x%x\n", __func__, e_ctrl->i2c_client.client->addr); - e_ctrl->i2c_client.client->addr+=0x2; - #endif - #endif - #endif -// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up - } - if (emap[j].pageen.valid_size) { - e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; - rc = e_ctrl->i2c_client.i2c_func_tbl->i2c_write( - &(e_ctrl->i2c_client), emap[j].pageen.addr, - 0, emap[j].pageen.data_t); - if (rc < 0) { - pr_err("%s: page disable failed\n", __func__); - return rc; - } } if (emap[j].pageen.valid_size) { e_ctrl->i2c_client.addr_type = emap[j].pageen.addr_t; @@ -428,71 +389,13 @@ static struct msm_cam_clk_info cam_8960_clk_info[] = { }; static struct msm_cam_clk_info cam_8974_clk_info[] = { -// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up - [SENSOR_CAM_MCLK] = {"cam_src_clk", 24000000}, -// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up + [SENSOR_CAM_MCLK] = {"cam_src_clk", 19200000}, [SENSOR_CAM_CLK] = {"cam_clk", 0}, }; static struct v4l2_subdev_core_ops msm_eeprom_subdev_core_ops = { .ioctl = msm_eeprom_subdev_ioctl, }; -// [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up -#if defined(CONFIG_IMX111)|| defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) -static int msm_eeprom_check_CRC(struct msm_eeprom_ctrl_t *e_ctrl) -{ - int CRC_code, CRC_sum, counter; - - // (CRC MSB - 0x53, 0x7E << 8 ) + (CRC LSB - 0x53, 0x7F) = SUM( 0x50, 0x0A ~ 0x53,0x7D) - CRC_code = (e_ctrl->memory_data[0x037E] << 8) + e_ctrl->memory_data[0x037F]; - CRC_sum = 0; - - for( counter = 0x000A; counter <= 0x037D; counter++ ) { - CRC_sum += e_ctrl->memory_data[counter]; - } - - CRC_sum &= 0xffff; - - if( CRC_code != CRC_sum ) { - // CRC error - pr_err("%s IMX111 EEPROM CRC error for 5100K! CRC = 0x%04x / 0x%04x\n", __func__, CRC_code, CRC_sum); - return EIO; - } else { - CDBG("%s CRC for 5100K - OK = 0x%04x / 0x%04x", __func__, CRC_code, CRC_sum); - } - - // (CRC MSB - 0x53, 0xC4 << 8 ) + (CRC LSB - 0x53, 0xC5) = SUM( 0x53, 0x80 ~ 0x53,0xC3) - CRC_code = (e_ctrl->memory_data[0x03C4] << 8) + e_ctrl->memory_data[0x03C5]; - CRC_sum = 0; - - for( counter = 0x0380; counter <= 0x03C3; counter++ ) { - CRC_sum += e_ctrl->memory_data[counter]; - } - - CRC_sum &= 0xffff; - - if( CRC_code != CRC_sum ) { - // CRC error - pr_err("%s IMX111 EEPROM CRC error for 2856K! CRC = 0x%04x / 0x%04x\n", __func__, CRC_code, CRC_sum); - return EIO; - } else { - CDBG("%s CRC for 2856K - OK = 0x%04x / 0x%04x", __func__, CRC_code, CRC_sum); - } - - return 0; -} -#endif -// [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up -// [LGE_UPDATE_S] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) -static int __init camera_information_setup(char *cam_info) -{ - lge_camera_info = cam_info; - printk(KERN_INFO "Camera Info : %s\n", lge_camera_info); - return 1; -} -__setup("lge.camera=", camera_information_setup); -// [LGE_UPDATE_E] [yt.jeon@lge.com] [2014-01-06] use cmdline info (lge.camera) - static struct v4l2_subdev_ops msm_eeprom_subdev_ops = { .core = &msm_eeprom_subdev_core_ops, @@ -541,7 +444,7 @@ int32_t msm_eeprom_i2c_probe(struct i2c_client *client, } power_info = &e_ctrl->eboard_info->power_info; - e_ctrl->eboard_info->i2c_slaveaddr = temp<<1; //LGE_UPDATE makes 8bit for i2c driver 2013-11-26 yt.jeon@lge.com + e_ctrl->eboard_info->i2c_slaveaddr = temp; e_ctrl->i2c_client.client = client; e_ctrl->is_supported = 0; @@ -587,29 +490,6 @@ int32_t msm_eeprom_i2c_probe(struct i2c_client *client, for (j = 0; j < e_ctrl->num_bytes; j++) CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]); - // [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up -#if defined(CONFIG_MACH_MSM8X10_W5_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5DS_GLOBAL_COM) || defined(CONFIG_MACH_MSM8X10_W5TS_GLOBAL_COM) - if(lge_camera_info){ - if(!strncmp(lge_camera_info, "imx111", 6)){ - pr_err("%s (GLOBAL) call msm_eeprom_check_CRC\n", __func__); - rc = msm_eeprom_check_CRC(e_ctrl); - if (rc < 0) { - pr_err("%s read_eeprom_memory failed\n", __func__); - goto memdata_free; - } - } - } -#else - #if defined(CONFIG_IMX111) - pr_err("%s call msm_eeprom_check_CRC\n", __func__); - rc = msm_eeprom_check_CRC(e_ctrl); - if (rc < 0) { - pr_err("%s read_eeprom_memory failed\n", __func__); - goto memdata_free; - } - #endif -#endif - // [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); @@ -1026,15 +906,6 @@ static int32_t msm_eeprom_platform_probe(struct platform_device *pdev) pr_err("%s line %d\n", __func__, __LINE__); for (j = 0; j < e_ctrl->num_bytes; j++) CDBG("memory_data[%d] = 0x%X\n", j, e_ctrl->memory_data[j]); - // [LGE_UPDATE_S] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up - #if defined(CONFIG_IMX111) - rc = msm_eeprom_check_CRC(e_ctrl); - if (rc < 0) { - pr_err("%s read_eeprom_memory failed\n", __func__); - goto memdata_free; - } - #endif - // [LGE_UPDATE_E] [hyungtae.lee@lge.com] [2013-05-24] EEPROM ( of Y412B ) bring-up rc = msm_camera_power_down(power_info, e_ctrl->eeprom_device_type, &e_ctrl->i2c_client); From ba5c2f712ff2614f8fa296a524f391b65be02cb3 Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 20 Nov 2014 20:10:13 +0300 Subject: [PATCH 033/104] Revert "Revert "msm-camera: Fixed MIPI raw snaspshot issue 8x10"" This reverts commit 5f94b0527f4a08d7a1b3afc6ddd8277d058c99de. --- .../media/platform/msm/camera_v2/isp/msm_isp32.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index e0c32417a889..3065f625ead2 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -154,6 +154,13 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w_mb(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); //Aravind msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18); //Aravind + + msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); + //Aravind start vfe_dev->error_info.error_mask1 =0; vfe_dev->error_info.error_mask0 =0; @@ -161,8 +168,13 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) vfe_dev->error_info.violation_status =0; //Aravind end #else - msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); - msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); + msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0x0, vfe_dev->vfe_base+0x6FC); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(1)); + msm_camera_io_w( 0x10000000,vfe_dev->vfe_base + VFE32_RDI_BASE(2)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); #endif } From 0ffb61f20dba03d6d4023e7756a4593d3dea675b Mon Sep 17 00:00:00 2001 From: Justin Philip Date: Tue, 3 Jun 2014 16:03:24 +0530 Subject: [PATCH 034/104] msm: mdss: Allow smp change during composition switch The composition fallback mechanism might not ensure unsetting of the pipe whose prepare call failed due to smp configuration change, typically in the cases where change in composition requests same pipe. This scenario is a deadlock where composition switch does not happen, due to these pipe failures. To handle such cases, for changed SMP request for a pipe doing non backend composition, allow smp configuration to happen, so that the composition could be successfully switched, thereby preventing the deadlock. Change-Id: I2d29ac6591671494abc7c4caf7c6c53f058d12f6 Signed-off-by: Justin Philip --- drivers/video/msm/mdss/mdss_mdp_pipe.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c index 82956e88551e..f6c9a2c5fbe1 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pipe.c +++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c @@ -49,7 +49,7 @@ static inline u32 mdss_mdp_pipe_read(struct mdss_mdp_pipe *pipe, u32 reg) } static u32 mdss_mdp_smp_mmb_reserve(struct mdss_mdp_pipe_smp_map *smp_map, - size_t n) + size_t n, bool force_alloc) { u32 i, mmb; u32 fixed_cnt = bitmap_weight(smp_map->fixed, SMP_MB_CNT); @@ -67,7 +67,7 @@ static u32 mdss_mdp_smp_mmb_reserve(struct mdss_mdp_pipe_smp_map *smp_map, * that calls for change in smp configuration (addition/removal * of smp blocks), so that fallback solution happens. */ - if (i != 0 && n != i) { + if (i != 0 && n != i && !force_alloc) { pr_debug("Can't change mmb config, num_blks: %d alloc: %d\n", n, i); return 0; @@ -190,6 +190,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) struct mdss_mdp_plane_sizes ps; int i; int rc = 0, rot_mode = 0, wb_mixer = 0; + bool force_alloc = 0; u32 nlines, format, seg_w; u16 width; @@ -273,6 +274,14 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) if (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) wb_mixer = 1; + /* + * Don't want to allow SMP changes for backend composition pipes + * inorder to preserve SMPs as much as possible. + * On the contrary for non backend composition pipes we should + * allow SMP allocations to prevent composition failures. + */ + force_alloc = !(pipe->flags & MDP_BACKEND_COMPOSITION); + mutex_lock(&mdss_mdp_smp_lock); for (i = (MAX_PLANES - 1); i >= ps.num_planes; i--) { @@ -303,7 +312,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) pr_debug("reserving %d mmb for pnum=%d plane=%d\n", num_blks, pipe->num, i); reserved = mdss_mdp_smp_mmb_reserve(&pipe->smp_map[i], - num_blks); + num_blks, force_alloc); if (reserved < num_blks) break; } From 0fd0d19dce84c9a8a2ec8cc9745880789ace3d05 Mon Sep 17 00:00:00 2001 From: Justin Philip Date: Tue, 12 Aug 2014 13:46:54 +0530 Subject: [PATCH 035/104] mdss: Add MDP_SMP_FORCE_ALLOC mdp flag MDP_SMP_FORCE_ALLOC flag is used to allow SMP allocations even when there is mismatch between allocated and requested SMPs for a pipe. User space can make use of this flag in extreme scenarios where SMP allocations need to pass like GPU composition and playback of protected or secure videos. Change-Id: I369b4361e7e2bbfc8150add467678c4ef8d5cfb6 Signed-off-by: Justin Philip --- drivers/video/msm/mdss/mdss_mdp_pipe.c | 8 +------- include/linux/msm_mdp.h | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c index f6c9a2c5fbe1..e432a178660a 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pipe.c +++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c @@ -274,13 +274,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) if (pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK) wb_mixer = 1; - /* - * Don't want to allow SMP changes for backend composition pipes - * inorder to preserve SMPs as much as possible. - * On the contrary for non backend composition pipes we should - * allow SMP allocations to prevent composition failures. - */ - force_alloc = !(pipe->flags & MDP_BACKEND_COMPOSITION); + force_alloc = pipe->flags & MDP_SMP_FORCE_ALLOC; mutex_lock(&mdss_mdp_smp_lock); diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index a7430d9806fb..02523d03802e 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -197,6 +197,7 @@ enum { #define MDP_MEMORY_ID_TYPE_FB 0x00001000 #define MDP_BWC_EN 0x00000400 #define MDP_DECIMATION_EN 0x00000800 +#define MDP_SMP_FORCE_ALLOC 0x00200000 #define MDP_TRANSP_NOP 0xffffffff #define MDP_ALPHA_NOP 0xff From 059b47e7f0dd9fdf6af1772ac717214fc2920e62 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 7 Dec 2014 18:06:16 +0300 Subject: [PATCH 036/104] fix argument to 'sizeof' in 'snprintf' call is the same expression as the destination --- drivers/input/touchscreen/ist30xx_factory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/ist30xx_factory.c b/drivers/input/touchscreen/ist30xx_factory.c index 1c36fc989a7e..edd0e05aec65 100644 --- a/drivers/input/touchscreen/ist30xx_factory.c +++ b/drivers/input/touchscreen/ist30xx_factory.c @@ -361,7 +361,7 @@ ssize_t factory_back_key_state_show(struct device *dev, DMSG("[TSP] back tkey state: %d\n", key_state); - return snprintf(buf, sizeof(buf), "%d\n", key_state); + return snprintf(buf, sizeof(*buf), "%d\n", key_state); } /* /sys/class/factory/tkey/tkey_menu */ From 9a0c91deca0f8df3f39d23078336625150e22c67 Mon Sep 17 00:00:00 2001 From: Shivaraj Shetty Date: Thu, 10 Apr 2014 19:31:43 +0530 Subject: [PATCH 037/104] mdss: Add newline to VSYNC timestamp for mdp3 driver This makes sure that the userspace correctly interprets the end of the vsync timestamp. CRs-Fixed: 639039 Change-Id: I8a0810283a42a9c39a3c8fe36b862bbfd3ecdbea Signed-off-by: Shivaraj Shetty --- drivers/video/msm/mdss/mdp3_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 961f7e2bc8a2..763c164871bd 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -297,7 +297,7 @@ static ssize_t mdp3_vsync_show_event(struct device *dev, vsync_ticks = ktime_to_ns(mdp3_session->vsync_time); pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks); - rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu", vsync_ticks); + rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks); return rc; } From e545c269beb30258687dc6b72ca39c7821775809 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 4 Jan 2015 15:31:26 +0300 Subject: [PATCH 038/104] input: lge_touch: Generate KEY_POWER events for wakeup gestures --- drivers/input/touchscreen/lge_touch_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/touchscreen/lge_touch_core.c b/drivers/input/touchscreen/lge_touch_core.c index e00c03b09001..df53e22ad35f 100644 --- a/drivers/input/touchscreen/lge_touch_core.c +++ b/drivers/input/touchscreen/lge_touch_core.c @@ -3137,6 +3137,11 @@ void send_uevent_lpwg(struct i2c_client* client, int type) && atomic_read(&ts->state.uevent_state) == UEVENT_IDLE) { atomic_set(&ts->state.uevent_state, UEVENT_BUSY); send_uevent(lpwg_uevent[type-1]); + if (type == LPWG_DOUBLE_TAP) { + input_report_key(ts->input_dev, KEY_POWER, BUTTON_PRESSED); + input_report_key(ts->input_dev, KEY_POWER, BUTTON_RELEASED); + input_sync(ts->input_dev); + } } } @@ -4215,6 +4220,8 @@ static int touch_probe(struct i2c_client *client, const struct i2c_device_id *id ts->input_dev->name = "touch_dev"; set_bit(EV_SYN, ts->input_dev->evbit); set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(KEY_POWER, ts->input_dev->keybit); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); #endif From 38f6f541b2bb36c362c7e7c19dc58e88fb9682bb Mon Sep 17 00:00:00 2001 From: vm03 Date: Mon, 5 Jan 2015 21:37:11 +0300 Subject: [PATCH 039/104] Fix compile nfc Many thanks to Quarx2k ;) --- include/linux/Kbuild | 1 + include/linux/nfc/Kbuild | 2 ++ include/linux/nfc/pn544.h | 12 ++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 include/linux/nfc/Kbuild diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 4640d3bd7d04..8111fd4cb71c 100755 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -17,6 +17,7 @@ header-y += netfilter_arp/ header-y += netfilter_bridge/ header-y += netfilter_ipv4/ header-y += netfilter_ipv6/ +header-y += nfc/ header-y += usb/ header-y += wimax/ header-y += mfd/ diff --git a/include/linux/nfc/Kbuild b/include/linux/nfc/Kbuild new file mode 100644 index 000000000000..9781cfb191fc --- /dev/null +++ b/include/linux/nfc/Kbuild @@ -0,0 +1,2 @@ +header-y += pn544.h + diff --git a/include/linux/nfc/pn544.h b/include/linux/nfc/pn544.h index 7ab8521f2347..785b3fafa43c 100644 --- a/include/linux/nfc/pn544.h +++ b/include/linux/nfc/pn544.h @@ -25,6 +25,18 @@ #include +#define PN544_MAGIC 0xE9 + +/* + * PN544 power control via ioctl + * PN544_SET_PWR(0): power off + * PN544_SET_PWR(1): power on + * PN544_SET_PWR(2): reset and power on with firmware download enabled + */ +#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, unsigned int) + +#define PN544_HW_REVISION _IOR(PN544_MAGIC, 0x02, unsigned int) + #define PN544_DRIVER_NAME "pn544" #define PN544_MAXWINDOW_SIZE 7 #define PN544_WINDOW_SIZE 4 From b66aaf48f8a87f9b1b226dde62572988f67d54e2 Mon Sep 17 00:00:00 2001 From: vm03 Date: Mon, 22 Dec 2014 11:07:21 +0300 Subject: [PATCH 040/104] nfc: lge: add pn544 --- drivers/nfc/Kconfig | 99 +++- drivers/nfc/Makefile | 7 +- drivers/nfc/nfc-nci.c | 358 +++----------- drivers/nfc/pn544_lge.c | 760 ++++++++++++++++++++++++++++++ drivers/nfc/pn544_lge_hwadapter.c | 80 ++++ drivers/nfc/pn544_lge_hwadapter.h | 16 + include/linux/nfc/pn544_lge.h | 101 ++++ 7 files changed, 1119 insertions(+), 302 deletions(-) create mode 100644 drivers/nfc/pn544_lge.c create mode 100644 drivers/nfc/pn544_lge_hwadapter.c create mode 100644 drivers/nfc/pn544_lge_hwadapter.h create mode 100644 include/linux/nfc/pn544_lge.h diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 3039b0de94b6..567e8ebfa5fd 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -4,9 +4,26 @@ menu "Near Field Communication (NFC) devices" depends on NFC +endmenu + +config LGE_NFC + bool "NFC Device Enabled for LGE" + default n + +config LGE_NFC_PN544_C2 + bool "PN544 C2 NFC driver" + depends on I2C + select CRC_CCITT + default n + ---help--- + Say yes if you want PN544 Near Field Communication driver. + This is for i2c connected version. If unsure, say N here. + + To compile this driver as a module, choose m here. The module will + be called pn544. -config PN544_NFC - tristate "PN544 NFC driver" +config LGE_NFC_PN544_C3 + bool "PN544 C3 NFC driver" depends on I2C select CRC_CCITT default n @@ -17,6 +34,75 @@ config PN544_NFC To compile this driver as a module, choose m here. The module will be called pn544. +config LGE_NFC_PN547 + bool "PN547 NFC driver" + depends on I2C + select CRC_CCITT + default n + ---help--- + Say yes if you want PN547 Near Field Communication driver. + This is for i2c connected version. If unsure, say N here. + + To compile this driver as a module, choose m here. The module will + be called pn547. + +config LGE_NFC_PRESTANDBY + bool "NFC STANBY MODE" + default n + ---help--- + Say yes if you want to change a power state of pn544 to stanby mode in Kernel. + If unsure, say N here. + +config LGE_NFC_MULTICORE_FASTBOOT + bool "NFC MultiThread" + default n + ---help--- + Say yes if multi thread is applied for pre-standby. + If unsure, say N here. + +config LGE_NFC_SET_IRQ_WAKEUP + bool "NFC Set IRQ wakeup when NFC ON" + default n + ---help--- + Say yes if Set IRQ Wakeup NFC ON. + If unsure, say N here. + +config LGE_NFC_DEBUG_MESSAGE + bool "NFC Debug Message" + default n + +config LGE_NFC_HW_QCT_MSM72X7A + bool "Qualcom MSM72X7A Chipset" + default n + +config LGE_NFC_HW_QCT_APQ8064 + bool "Qualcom APQ8064 Chipset" + default n + +config LGE_NFC_HW_QCT_MSM8960 + bool "Qualcom APQ8960 Chipset" + default n + +config LGE_NFC_HW_QCT_MSM8930 + bool "Qualcom MSM8930 Chipset" + default n + +config LGE_NFC_HW_QCT_MSM8660 + bool "Qualcom MSM8660 Chipset" + default n + +config LGE_NFC_HW_QCT_MSM8255 + bool "Qualcom MSM8255 Chipset" + default n + +config LGE_NFC_HW_TI_OMAP4430 + bool "TI OMAP4430 Chipset" + default n + +config LGE_NFC_HW_NV_AP3X + bool "Nvidia AP3X Chipset" + default n + config NFC_PN533 tristate "NXP PN533 USB driver" depends on USB @@ -38,13 +124,4 @@ config NFC_WILINK Say Y here to compile support for Texas Instrument's NFC WiLink driver into the kernel or say M to compile it as module. -endmenu -config NFC_QNCI - bool "Qualcomm NCI based NFC Controller Driver for qca199x" - depends on I2C - select CRC_CCITT - help - This enables the NFC driver for QCA199x based devices. - This is for i2c connected version. NCI protocol logic - resides in the usermode and it has no other NFC dependencies. diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 35a71e80dba0..0500af67f795 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,9 +2,8 @@ # Makefile for nfc devices # -obj-$(CONFIG_PN544_NFC) += pn544.o -obj-$(CONFIG_NFC_PN533) += pn533.o -obj-$(CONFIG_NFC_WILINK) += nfcwilink.o -obj-$(CONFIG_NFC_QNCI) += nfc-nci.o +obj-$(CONFIG_LGE_NFC) := pn544_lge.o +#obj-$(CONFIG_LGE_NFC) := pn544.o +obj-$(CONFIG_LGE_NFC) += pn544_lge_hwadapter.o ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c index b808f97fbcb3..99e17a60e76b 100644 --- a/drivers/nfc/nfc-nci.c +++ b/drivers/nfc/nfc-nci.c @@ -33,7 +33,7 @@ struct qca199x_platform_data { unsigned int dis_gpio; unsigned int ven_gpio; unsigned int reg; - const char *clk_src_name; + const char *clk_src; unsigned int clk_src_gpio; }; @@ -44,19 +44,15 @@ static struct of_device_id msm_match_table[] = { MODULE_DEVICE_TABLE(of, msm_match_table); -#define MAX_BUFFER_SIZE (780) -#define PACKET_MAX_LENGTH (258) +#define MAX_BUFFER_SIZE (780) /* Read data */ #define PACKET_HEADER_SIZE_NCI (4) -#define PACKET_TYPE_NCI (16) -#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255) -#define MAX_QCA_REG (116) -/* will timeout in approx. 100ms as 10us steps */ -#define NTF_TIMEOUT (10000) -#define CORE_RESET_RSP_GID (0x60) -#define CORE_RESET_OID (0x00) -#define CORE_RST_NTF_LENGTH (0x02) +#define PACKET_TYPE_NCI (16) +#define MAX_PACKET_SIZE (PACKET_HEADER_SIZE_NCI + 255) +#define MAX_QCA_REG (116) +static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len); +static int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr); struct qca199x_dev { wait_queue_head_t read_wq; @@ -67,23 +63,11 @@ struct qca199x_dev { unsigned int dis_gpio; unsigned int ven_gpio; bool irq_enabled; - bool sent_first_nci_write; spinlock_t irq_enabled_lock; unsigned int count_irq; enum nfcc_state state; - unsigned int clk_src_gpio; - const char *clk_src_name; - struct clk *s_clk; - bool clk_run; }; -static int nfc_i2c_write(struct i2c_client *client, u8 *buf, int len); -static int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr); -static int qca199x_clock_select(struct qca199x_dev *qca199x_dev); -static int qca199x_clock_deselect(struct qca199x_dev *qca199x_dev); - - - /* * To allow filtering of nfc logging from user. This is set via * IOCTL NFC_KERNEL_LOGGING_MODE. @@ -221,33 +205,23 @@ static ssize_t nfc_read(struct file *filp, char __user *buf, /* NORMAL NCI Behaviour */ /* Read the header */ ret = i2c_master_recv(qca199x_dev->client, len, PAYLOAD_HEADER_LENGTH); - /* - We ignore all packets of length PAYLOAD_HEADER_LENGTH - or less (i.e <=3). In this case return a total length - of ZERO. So ALL PACKETS MUST HAVE A PAYLOAD. - If ret < 0 then this is an error code. - */ if (ret != PAYLOAD_HEADER_LENGTH) { - if (ret < 0) - total = ret; - else - total = 0; + total = 0; goto err; } length = len[PAYLOAD_HEADER_LENGTH - 1]; - if (length == 0) { - ret = 0; - total = ret; - goto err; - } + if (length == 0) + total = 0; /** make sure full packet fits in the buffer **/ if ((length > 0) && ((length + PAYLOAD_HEADER_LENGTH) <= count)) { /* Read the packet */ ret = i2c_master_recv(qca199x_dev->client, tmp, (length + PAYLOAD_HEADER_LENGTH)); - total = ret; - if (ret < 0) + if (ret < 0) { + total = 0; goto err; + } + total = (length + PAYLOAD_HEADER_LENGTH); } if (total > 0) { @@ -258,57 +232,14 @@ static ssize_t nfc_read(struct file *filp, char __user *buf, total = -EFAULT; } } + mutex_unlock(&qca199x_dev->read_mutex); err: + if (ret < 0) mutex_unlock(&qca199x_dev->read_mutex); done: return total; } -/* - Local routine to read from nfcc buffer. This is called to clear any - pending receive messages in the nfcc's read buffer, which may be there - following a POR. In this way, the upper layers (Device Transport) will - associate the next rsp/ntf nci message with the next nci command to the - nfcc. Otherwise, the DT may interpret a ntf from the nfcc as being from - the nci core reset command when in fact it was already present in the - nfcc read buffer following a POR. -*/ - -int nfcc_read_buff_svc(struct qca199x_dev *qca199x_dev) -{ - unsigned char tmp[PACKET_MAX_LENGTH]; - unsigned char len[PAYLOAD_HEADER_LENGTH]; - int total, length, ret; - total = 0; - length = 0; - mutex_lock(&qca199x_dev->read_mutex); - memset(tmp, 0, sizeof(tmp)); - memset(len, 0, sizeof(len)); - - /* Read the header */ - ret = i2c_master_recv(qca199x_dev->client, len, PAYLOAD_HEADER_LENGTH); - if (ret < PAYLOAD_HEADER_LENGTH) { - total = ret; - goto leave; - } - length = len[PAYLOAD_HEADER_LENGTH - 1]; - if (length == 0) { - ret = PAYLOAD_HEADER_LENGTH; - total = ret; - goto leave; - } - /** make sure full packet fits in the buffer **/ - if ((length > 0) && ((length + PAYLOAD_HEADER_LENGTH) <= PACKET_MAX_LENGTH)) { - /* Read the packet */ - ret = i2c_master_recv(qca199x_dev->client, tmp, (length + - PAYLOAD_HEADER_LENGTH)); - total = ret; - } -leave: - mutex_unlock(&qca199x_dev->read_mutex); - return total; -} - static ssize_t nfc_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset) { @@ -316,7 +247,6 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf, char tmp[MAX_BUFFER_SIZE]; int ret = 0; enum ehandler_mode dmode; - int nfcc_buffer = 0; if (count > MAX_BUFFER_SIZE) { dev_err(&qca199x_dev->client->dev, "out of memory\n"); @@ -327,28 +257,10 @@ static ssize_t nfc_write(struct file *filp, const char __user *buf, "nfc-nci write: failed to copy from user space\n"); return -EFAULT; } - /* - A catch for when the DT is sending the initial NCI write - following a hardware POR. In this case we should clear any - pending messages in nfcc buffer and open the interrupt gate - for new messages coming from the nfcc. - */ - if ((qca199x_dev->sent_first_nci_write == false) && - (qca199x_dev->irq_enabled == false)) { - /* check rsp/ntf from nfcc read-side buffer */ - nfcc_buffer = nfcc_read_buff_svc(qca199x_dev); - /* There has been an error while reading from nfcc */ - if (nfcc_buffer < 0) { - dev_err(&qca199x_dev->client->dev, - "nfc-nci write: error while servicing nfcc read buffer\n"); - } - qca199x_dev->sent_first_nci_write = true; - qca199x_enable_irq(qca199x_dev); - } mutex_lock(&qca199x_dev->read_mutex); dmode = device_mode.handle_flavour; /* FTM-DIRECT-I2C RD/WR MODE */ - /* This is a special FTM-i2c mode case, where tester is not using NCI */ + /* This is a special FTM-i2c mode case,where tester is not using NCI */ if ((dmode == UNSOLICITED_FTM_RAW_MODE) || (dmode == SOLICITED_FTM_RAW_MODE)) { /* Read From Register */ @@ -466,9 +378,6 @@ int nfc_ioctl_power_states(struct file *filp, unsigned int cmd, struct qca199x_dev *qca199x_dev = filp->private_data; if (arg == 0) { - r = qca199x_clock_select(qca199x_dev); - if (r < 0) - goto err_req; gpio_set_value(qca199x_dev->dis_gpio, 0); r = gpio_direction_output(qca199x_dev->dis_gpio, 1); if (r) { @@ -480,23 +389,6 @@ int nfc_ioctl_power_states(struct file *filp, unsigned int cmd, gpio_set_value(qca199x_dev->dis_gpio, 0); msleep(20); } else if (arg == 1) { - /* - We are attempting a hardware reset so let us disable - interrupts to avoid spurious notifications to upper - layers. - */ - qca199x_disable_irq(qca199x_dev); - /* Deselection of clock */ - r = qca199x_clock_deselect(qca199x_dev); - if (r < 0) - goto err_req; - /* - Also, set flag for initial NCI write following resetas - may wish to do some house keeping. Ensure no pending - messages in NFCC buffers which may be wrongly - construed as response to initial message - */ - qca199x_dev->sent_first_nci_write = false; gpio_set_value(qca199x_dev->dis_gpio, 0); r = gpio_direction_output(qca199x_dev->dis_gpio, 1); if (r) { @@ -510,12 +402,10 @@ int nfc_ioctl_power_states(struct file *filp, unsigned int cmd, } else if (arg == 2) { mutex_lock(&qca199x_dev->read_mutex); r = nfcc_initialise(qca199x_dev->client, 0xE); - /* Also reset first NCI write */ - qca199x_dev->sent_first_nci_write = false; mutex_unlock(&qca199x_dev->read_mutex); if (r) { dev_err(&qca199x_dev->client->dev, - "nfc_ioctl_power_states: request nfcc initialise failed\n"); + "nfc-nci probe: request nfcc initialise failed\n"); goto err_req; } } else if (arg == 3) { @@ -646,6 +536,8 @@ int nfc_ioctl_nfcc_version(struct file *filp, unsigned int cmd, return raw_chip_version; } + + /* * Inside nfc_ioctl_kernel_logging * @@ -772,211 +664,72 @@ static int nfcc_initialise(struct i2c_client *client, unsigned short curr_addr) unsigned char raw_1P8_PAD_CFG_CLK_REQ[] = {0xA5, 0x1}; unsigned char raw_1P8_PAD_CFG_PWR_REQ[] = {0xA7, 0x1}; unsigned char buf = 0; - bool core_reset_completed = false; - unsigned char rsp[6]; - int time_taken = 0; - int ret = 0; client->addr = curr_addr; r = i2c_master_send(client, &buf, 1); - if (r < 0) - goto err_init; - buf = 0; r = i2c_master_recv(client, &buf, 1); - if (r < 0) - goto err_init; - if (0x10 != (0x10 & buf)) { RAW(s73, 0x02); r = nfc_i2c_write(client, &raw_s73[0], sizeof(raw_s73)); - if (r < 0) - goto err_init; - usleep(1000); RAW(1p8_CONTROL_011, XTAL_CLOCK | 0x01); r = nfc_i2c_write(client, &raw_1p8_CONTROL_011[0], sizeof(raw_1p8_CONTROL_011)); - if (r < 0) - goto err_init; - usleep(1000); RAW(1P8_CONTROL_010, (0x8)); r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0], sizeof(raw_1P8_CONTROL_010)); - if (r < 0) - goto err_init; usleep(10000); /* 10ms wait */ RAW(1P8_CONTROL_010, (0xC)); r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0], sizeof(raw_1P8_CONTROL_010)); - if (r < 0) - goto err_init; - usleep(100); /* 100uS wait */ RAW(1P8_X0_0B0, (FREQ_SEL_19)); r = nfc_i2c_write(client, &raw_1P8_X0_0B0[0], sizeof(raw_1P8_X0_0B0)); - if (r < 0) - goto err_init; - usleep(1000); /* PWR_EN = 1 */ RAW(1P8_CONTROL_010, (0xd)); r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0], sizeof(raw_1P8_CONTROL_010)); - if (r < 0) - goto err_init; - - usleep(20000); /* 20ms wait */ /* LS_EN = 1 */ RAW(1P8_CONTROL_010, 0xF); r = nfc_i2c_write(client, &raw_1P8_CONTROL_010[0], sizeof(raw_1P8_CONTROL_010)); - if (r < 0) - goto err_init; - usleep(20000); /* 20ms wait */ /* Enable the PMIC clock */ RAW(1P8_PAD_CFG_CLK_REQ, (0x1)); r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_CLK_REQ[0], sizeof(raw_1P8_PAD_CFG_CLK_REQ)); - if (r < 0) - goto err_init; - usleep(1000); RAW(1P8_PAD_CFG_PWR_REQ, (0x1)); r = nfc_i2c_write(client, &raw_1P8_PAD_CFG_PWR_REQ[0], sizeof(raw_1P8_PAD_CFG_PWR_REQ)); - if (r < 0) - goto err_init; - usleep(1000); RAW(slave2, 0x10); r = nfc_i2c_write(client, &raw_slave2[0], sizeof(raw_slave2)); - if (r < 0) - goto err_init; - usleep(1000); RAW(slave1, NCI_I2C_SLAVE); r = nfc_i2c_write(client, &raw_slave1[0], sizeof(raw_slave1)); - if (r < 0) - goto err_init; - usleep(1000); /* QCA199x NFCC CPU should now boot... */ r = i2c_master_recv(client, &raw_slave1_rd, 1); /* Talk on NCI slave address NCI_I2C_SLAVE 0x2C*/ client->addr = NCI_I2C_SLAVE; - - /* - Start with small delay and then we will poll until we - get a core reset notification - This is time for chip - & NFCC controller to come-up. - */ - usleep(1000); /* 1 ms */ - - do { - ret = i2c_master_recv(client, rsp, 5); - /* Found core reset notification */ - if (((rsp[0] == CORE_RESET_RSP_GID) && - (rsp[1] == CORE_RESET_OID) && - (rsp[2] == CORE_RST_NTF_LENGTH)) - || time_taken == NTF_TIMEOUT) { - core_reset_completed = true; - } - usleep(10); /* 10us sleep before retry */ - time_taken++; - } while (!core_reset_completed); r = 0; } else { - goto err_init; - } - return r; -err_init: - r = 1; - dev_err(&client->dev, - "nfc-nci nfcc_initialise: failed. Check Hardware\n"); - return r; -} -/* - Routine to Select clocks -*/ -static int qca199x_clock_select(struct qca199x_dev *qca199x_dev) -{ - int r = 0; - - if (!strcmp(qca199x_dev->clk_src_name, "BBCLK2")) { - qca199x_dev->s_clk = - clk_get(&qca199x_dev->client->dev, "ref_clk"); - if (qca199x_dev->s_clk == NULL) - goto err_invalid_dis_gpio; - } else if (!strcmp(qca199x_dev->clk_src_name, "RFCLK3")) { - qca199x_dev->s_clk = - clk_get(&qca199x_dev->client->dev, "ref_clk_rf"); - if (qca199x_dev->s_clk == NULL) - goto err_invalid_dis_gpio; - } else if (!strcmp(qca199x_dev->clk_src_name, "GPCLK")) { - if (gpio_is_valid(qca199x_dev->clk_src_gpio)) { - qca199x_dev->s_clk = - clk_get(&qca199x_dev->client->dev, "core_clk"); - if (qca199x_dev->s_clk == NULL) - goto err_invalid_dis_gpio; - } else { - goto err_invalid_dis_gpio; - } - } else if (!strcmp(qca199x_dev->clk_src_name, "GPCLK2")) { - if (gpio_is_valid(qca199x_dev->clk_src_gpio)) { - qca199x_dev->s_clk = - clk_get(&qca199x_dev->client->dev, "core_clk_pvt"); - if (qca199x_dev->s_clk == NULL) - goto err_invalid_dis_gpio; - } else { - goto err_invalid_dis_gpio; - } - } else { - qca199x_dev->s_clk = NULL; - goto err_invalid_dis_gpio; - } - if (qca199x_dev->clk_run == false) { - r = clk_prepare_enable(qca199x_dev->s_clk); - if (r) - goto err_invalid_clk; - qca199x_dev->clk_run = true; - } - r = 0; - return r; - -err_invalid_clk: - r = -1; - return r; -err_invalid_dis_gpio: - r = -2; - return r; -} -/* - Routine to De-Select clocks -*/ - -static int qca199x_clock_deselect(struct qca199x_dev *qca199x_dev) -{ - int r = -1; - if (qca199x_dev->s_clk != NULL) { - if (qca199x_dev->clk_run == true) { - clk_disable_unprepare(qca199x_dev->s_clk); - qca199x_dev->clk_run = false; - } - return 0; + r = 1; } return r; } @@ -1002,10 +755,10 @@ static int nfc_parse_dt(struct device *dev, struct qca199x_platform_data *pdata) if ((!gpio_is_valid(pdata->irq_gpio))) return -EINVAL; - r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src_name); + r = of_property_read_string(np, "qcom,clk-src", &pdata->clk_src); - if ((!strcmp(pdata->clk_src_name, "GPCLK")) || - (!strcmp(pdata->clk_src_name, "GPCLK2"))) + if ((!strcmp(pdata->clk_src, "GPCLK")) || + (!strcmp(pdata->clk_src, "GPCLK2"))) pdata->clk_src_gpio = of_get_named_gpio(np, "qcom,clk-en-gpio", 0); @@ -1019,6 +772,7 @@ static int qca199x_probe(struct i2c_client *client, { int r = 0; int irqn = 0; + struct clk *nfc_clk = NULL; struct device_node *node = client->dev.of_node; struct qca199x_platform_data *platform_data; struct qca199x_dev *qca199x_dev; @@ -1056,13 +810,12 @@ static int qca199x_probe(struct i2c_client *client, "nfc-nci probe: failed to allocate memory for module data\n"); return -ENOMEM; } - qca199x_dev->client = client; if (gpio_is_valid(platform_data->irq_gpio)) { r = gpio_request(platform_data->irq_gpio, "nfc_irq_gpio"); if (r) { dev_err(&client->dev, "unable to request gpio [%d]\n", platform_data->irq_gpio); - goto err_free_dev; + goto err_irq; } r = gpio_direction_input(platform_data->irq_gpio); if (r) { @@ -1090,7 +843,7 @@ static int qca199x_probe(struct i2c_client *client, dev_err(&client->dev, "NFC: unable to request gpio [%d]\n", platform_data->dis_gpio); - goto err_irq; + goto err_free_dev; } r = gpio_direction_output(platform_data->dis_gpio, 1); if (r) { @@ -1103,17 +856,40 @@ static int qca199x_probe(struct i2c_client *client, dev_err(&client->dev, "dis gpio not provided\n"); goto err_irq; } - /* Get the clock source name and gpio from from Device Tree */ - qca199x_dev->clk_src_name = platform_data->clk_src_name; - qca199x_dev->clk_src_gpio = platform_data->clk_src_gpio; - qca199x_dev->clk_run = false; - r = qca199x_clock_select(qca199x_dev); - if (r != 0) { - if (r == -1) - goto err_clk; - else + gpio_set_value(platform_data->dis_gpio, 1);/* HPD */ + msleep(20); + gpio_set_value(platform_data->dis_gpio, 0);/* ULPM */ + if (!strcmp(platform_data->clk_src, "BBCLK2")) { + nfc_clk = clk_get(&client->dev, "ref_clk"); + if (nfc_clk == NULL) + goto err_dis_gpio; + } else if (!strcmp(platform_data->clk_src, "RFCLK3")) { + nfc_clk = clk_get(&client->dev, "ref_clk_rf"); + if (nfc_clk == NULL) goto err_dis_gpio; + } else if (!strcmp(platform_data->clk_src, "GPCLK")) { + if (gpio_is_valid(platform_data->clk_src_gpio)) { + nfc_clk = clk_get(&client->dev, "core_clk"); + if (nfc_clk == NULL) + goto err_dis_gpio; + } else { + goto err_dis_gpio; + } + } else if (!strcmp(platform_data->clk_src, "GPCLK2")) { + if (gpio_is_valid(platform_data->clk_src_gpio)) { + nfc_clk = clk_get(&client->dev, "core_clk_pvt"); + if (nfc_clk == NULL) + goto err_dis_gpio; + } else { + goto err_dis_gpio; + } + } else { + nfc_clk = NULL; } + r = clk_prepare_enable(nfc_clk); + if (r) + goto err_clk; + platform_data->ven_gpio = of_get_named_gpio(node, "qcom,clk-gpio", 0); @@ -1126,6 +902,7 @@ static int qca199x_probe(struct i2c_client *client, } r = gpio_direction_input(platform_data->ven_gpio); if (r) { + dev_err(&client->dev, "unable to set direction for gpio [%d]\n", platform_data->ven_gpio); @@ -1138,6 +915,7 @@ static int qca199x_probe(struct i2c_client *client, qca199x_dev->dis_gpio = platform_data->dis_gpio; qca199x_dev->irq_gpio = platform_data->irq_gpio; qca199x_dev->ven_gpio = platform_data->ven_gpio; + qca199x_dev->client = client; /* init mutex and queues */ init_waitqueue_head(&qca199x_dev->read_wq); @@ -1172,6 +950,12 @@ static int qca199x_probe(struct i2c_client *client, * for reading. It is cleared when all data has been read. */ device_mode.handle_flavour = UNSOLICITED_MODE; + r = nfcc_initialise(client, platform_data->reg); + if (r) { + dev_err(&client->dev, "nfc-nci probe: request nfcc initialise failed\n"); + goto err_nfcc_init_failed; + } + qca199x_dev->irq_enabled = true; r = request_irq(client->irq, qca199x_dev_irq_handler, IRQF_TRIGGER_RISING, client->name, qca199x_dev); @@ -1181,12 +965,12 @@ static int qca199x_probe(struct i2c_client *client, } qca199x_disable_irq(qca199x_dev); i2c_set_clientdata(client, qca199x_dev); - gpio_set_value(platform_data->dis_gpio, 1); dev_dbg(&client->dev, "nfc-nci probe: %s, probing qca1990 exited successfully\n", __func__); return 0; +err_nfcc_init_failed: err_request_irq_failed: misc_deregister(&qca199x_dev->qca199x_device); err_misc_register: @@ -1194,13 +978,13 @@ static int qca199x_probe(struct i2c_client *client, err_ven_gpio: gpio_free(platform_data->ven_gpio); err_clk: - qca199x_clock_deselect(qca199x_dev); + clk_disable_unprepare(nfc_clk); err_dis_gpio: r = gpio_direction_input(platform_data->dis_gpio); if (r) dev_err(&client->dev, "nfc-nci probe: Unable to set direction\n"); - if ((!strcmp(platform_data->clk_src_name, "GPCLK")) || - (!strcmp(platform_data->clk_src_name, "GPCLK2"))) { + if ((!strcmp(platform_data->clk_src, "GPCLK")) || + (!strcmp(platform_data->clk_src, "GPCLK2"))) { r = gpio_direction_input(platform_data->clk_src_gpio); if (r) dev_err(&client->dev, "nfc-nci probe: Unable to set direction\n"); diff --git a/drivers/nfc/pn544_lge.c b/drivers/nfc/pn544_lge.c new file mode 100644 index 000000000000..b7d4f8bb6220 --- /dev/null +++ b/drivers/nfc/pn544_lge.c @@ -0,0 +1,760 @@ +/* + * Copyright (C) 2010 NXP Semiconductors + */ + +#include +// seokmin.hong@lge.com header file added for removing depedency of platform and managing LGE modification +#include "pn544_lge_hwadapter.h" +/* LGE_CHANGE_S, [NFC][minwoo.kwon@lge.com], 2013-03-07, NFC Bring up */ +#include +/* LGE_CHANGE_E, [NFC][minwoo.kwon@lge.com], 2013-03-07, NFC Bring up */ + + +#ifdef CONFIG_LGE_NFC_MULTICORE_FASTBOOT +#include +#endif +/* LGE_CHANGE_E */ + +#define MAX_BUFFER_SIZE 512 +#define PN544_RESET_CMD 0 +#define PN544_DOWNLOAD_CMD 1 + +// LGE_START byunggu.kang@lge.com 2013-11-11 Modify for Balanced IRQ Reg/Dereg +#ifdef CONFIG_LGE_NFC_SET_IRQ_WAKEUP +static bool sIrqState = false; +#endif +// LGE_END byunggu.kang@lge.com 2013-07-21 Modify for Balanced IRQ Reg/Dereg + + +#ifdef LGE_NFC_READ_IRQ_MODIFY +bool do_reading = false;//DY_TEST +static bool cancle_read = false;//DY_TEST +#endif + +static int stReadIntFlag; +static struct i2c_client *pn544_client; + +static void pn544_parse_dt(struct device *dev, struct pn544_dev *pn544_dev) +{ + struct device_node *np = dev->of_node; + + /* irq gpio info */ + pn544_dev->ven_gpio = of_get_named_gpio_flags(np, "nxp,gpio_ven", 0, NULL); + pn544_dev->firm_gpio = of_get_named_gpio_flags(np, "nxp,gpio_mode", 0, NULL); + pn544_dev->irq_gpio = of_get_named_gpio_flags(np, "nxp,gpio_irq", 0, NULL); +} + +#ifdef CONFIG_LGE_NFC_PRESTANDBY +static struct mutex mode_mutex; +#endif + +static void pn544_disable_irq(struct pn544_dev *pn544_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&pn544_dev->irq_enabled_lock, flags); + if (pn544_dev->irq_enabled) { + disable_irq_nosync(pn544_get_irq_pin(pn544_dev)); +// 20120831, jh.heo@lge.com Fix to irq interrupt in sleep mode. +#if !defined(CONFIG_LGE_NFC_HW_QCT_MSM8660)&&!defined(CONFIG_LGE_NFC_HW_QCT_MSM8255) + disable_irq_wake(pn544_get_irq_pin(pn544_dev)); +#endif + pn544_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&pn544_dev->irq_enabled_lock, flags); +} + +static irqreturn_t pn544_dev_irq_handler(int irq, void *dev_id) +{ + struct pn544_dev *pn544_dev = dev_id; + + dprintk(PN544_DRV_NAME ":pn544_dev_irq_handler : %d\n", irq); + + pn544_disable_irq(pn544_dev); +#ifdef LGE_NFC_READ_IRQ_MODIFY + do_reading=1;//DY_TEST +#endif + + /* Wake up waiting readers */ + wake_up(&pn544_dev->read_wq); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_LGE_NFC_PRESTANDBY +#ifdef CONFIG_LGE_NFC_PN544_C2 +void pn544_factory_standby_set(void) +{ + int ret; + struct pn544_dev *pn544_dev; + struct pn544_i2c_platform_data *platform_data; + uint8_t EEDATA_WRITE[9] = {0x08, 0x00, 0x06, 0x00, 0x9E, 0xAA, 0x00, 0x01, 0x01}; + + platform_data = pn544_client->dev.platform_data; + + pn544_dev = i2c_get_clientdata(pn544_client); + // 1. Go To Dnld mode 2 + + gpio_set_value(pn544_dev->ven_gpio, 1); + gpio_set_value(pn544_dev->firm_gpio, 1); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + + // 2. I2c write + dprintk("%s Go To I2c write\n", __func__); + ret = 0; + ret = i2c_master_send(pn544_client, EEDATA_WRITE, 9); + if (ret != 9) { + dprintk(PN544_DRV_NAME ":%s : i2c_master_send returned %d\n", __func__, ret); + } + msleep(10); + + // 3. HW reset 1,0,1 + dprintk("%s Go To PN544 reset\n", __func__); + + //--> # reset 1 + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + + //--> # reset 0 + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + + //--> # reset 1 + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + + + // 4. power off + dprintk(PN544_DRV_NAME ":%s power off\n", __func__); + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); +} +#elif defined(CONFIG_LGE_NFC_PN544_C3) +static char pn544_standby_set_val1[6]={0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5}; +static char pn544_standby_set_val2[6]={0x05, 0x80, 0x83, 0x03, 0x5A, 0x0A}; +static char pn544_standby_set_val3[10]={0x09, 0x89, 0x83, 0x3F, 0x00, 0x9E, 0xAA, 0x01, 0xC2, 0x85}; +#define NFC_I2C_WRITE_RETRY_NUM 3 +static int __pn544_kwrite(void *dev, void* data, int size) +{ + struct pn544_dev *pn544_dev; + int ret = 0; + unsigned int retry = 0; + + if(dev != NULL) + pn544_dev = (struct pn544_dev*)dev; + + ret = i2c_master_send(pn544_client, data, size); + + while(retry != NFC_I2C_WRITE_RETRY_NUM) + { + msleep(10); + if(ret == size) + break; + ret = i2c_master_send(pn544_client, data, size); + retry++; + printk("%s i2c_master_send retry[%d]\n",__func__, retry); + } + + if(ret != size){ + pr_err("%s i2c_master_send failed[%d]\n", __func__, ret); + return -1; + } + + return 0; +} + +static int __pn544_kread(void *dev, unsigned int length) +{ + struct pn544_dev *pn544_dev = NULL; + char tmp[MAX_BUFFER_SIZE]; + int ret = 0; + int irq_gpio_val = 0; + + if(dev != NULL) + pn544_dev = (struct pn544_dev*)dev; + + mutex_lock(&mode_mutex); + + irq_gpio_val = gpio_get_value(pn544_dev->irq_gpio); + dprintk(PN544_DRV_NAME ":IRQ GPIO = %d\n", irq_gpio_val); + if (irq_gpio_val == 0) { + pn544_dev->irq_enabled = true; +#ifdef LGE_NFC_READ_IRQ_MODIFY + do_reading=0;//DY_TEST +#endif + enable_irq_wake(pn544_get_irq_pin(pn544_dev)); + enable_irq(pn544_get_irq_pin(pn544_dev)); +#ifdef LGE_NFC_READ_IRQ_MODIFY + ret = wait_event_interruptible(pn544_dev->read_wq, do_reading); +#else + ret = wait_event_interruptible(pn544_dev->read_wq, gpio_get_value(pn544_dev->irq_gpio)); +#endif + pn544_disable_irq(pn544_dev); + if(ret){ + mutex_unlock(&mode_mutex); + goto fail; + } + } + memset(tmp, 0x00, MAX_BUFFER_SIZE); + ret = i2c_master_recv(pn544_dev->client, tmp, length); + while(tmp[0]==0x51&&tmp[1]==0xFF&&tmp[2]==0xFF){ + ret = i2c_master_recv(pn544_dev->client, tmp, length); + printk("%s read retry!\n", __func__); + } + mutex_unlock(&mode_mutex); + printk("%s: read data : 0x%X 0x%X 0x%X 0x%X\n", __func__, tmp[0], tmp[1], tmp[2], tmp[3]); + if (ret < 0) { + dprintk("%s: i2c_master_recv returned %d\n", __func__, ret); + return ret; + } + if (ret > length) { + pr_err("%s: received too many bytes from i2c (%d)\n", __func__, ret); + return -EIO; + } + +fail: + return ret; +} + + +void pn544_factory_standby_set(void) +{ + int ret = 0; + struct pn544_dev *pn544_dev; + struct pn544_i2c_platform_data *platform_data; + + platform_data = pn544_client->dev.platform_data; + pn544_dev = i2c_get_clientdata(pn544_client); + // 1. Go To Dnld mode 2 + dprintk("%s Go To Dnld mode 2\n", __func__); + gpio_set_value(pn544_dev->ven_gpio, 0); + gpio_set_value(pn544_dev->firm_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + + // 2. I2c write + dprintk("%s Go To I2c write\n", __func__); + + ret = __pn544_kwrite(pn544_dev, pn544_standby_set_val1, 6); + if (ret == 0) { + printk("%s: standby write val1 success\n", __func__); + ret = __pn544_kread(pn544_dev, 4); + } else { + printk("%s: standby write val1 fail\n", __func__); + return; + } + + ret = __pn544_kwrite(pn544_dev, pn544_standby_set_val2, 6); + if (ret == 0) { + printk("%s: standby write val2 success\n", __func__); + ret = __pn544_kread(pn544_dev, 6); + } else { + printk("%s: standby write val2 fail\n", __func__); + return; + } + + ret = __pn544_kwrite(pn544_dev, pn544_standby_set_val3, 10); + if (ret == 0) { + printk("%s: standby write val3 success\n", __func__); + ret = __pn544_kread(pn544_dev, 7); + } else { + printk("%s: standby write val3 fail\n", __func__); + return; + } + + // 4. power off + dprintk(PN544_DRV_NAME ":%s power off\n", __func__); + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + + return; +} +#endif /* CONFIG_LGE_NFC_PN544_C2 & CONFIG_LGE_NFC_PN544_C3 */ +#endif /* CONFIG_LGE_NFC_PRESTANDBY */ + +/* LGE_CHANGE_S + * + * do device driver initialization + * using multithread during booting, + * in order to reduce booting time. + * + * byungchul.park@lge.com 20120328 + */ +#if defined(CONFIG_LGE_NFC_MULTICORE_FASTBOOT)&&defined(CONFIG_LGE_NFC_PRESTANDBY) +static int pn544_factory_standby_set_thread(void *arg) +{ + pn544_factory_standby_set(); + dprintk("%s end\n", __func__); + return 0; +} +#endif /* defined(CONFIG_LGE_NFC_MULTICORE_FASTBOOT)&&defined(CONFIG_LGE_NFC_PRESTANDBY) */ +/* LGE_CHANGE_E */ + +static ssize_t pn544_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct pn544_dev *pn544_dev = filp->private_data; + static char tmp[MAX_BUFFER_SIZE]; + int ret; + int irq_gpio_val = 0; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + pr_debug("%s : reading %zu bytes.\n", __func__, count); + + mutex_lock(&pn544_dev->read_mutex); + + if (!stReadIntFlag) { + irq_gpio_val = gpio_get_value(pn544_dev->irq_gpio); + dprintk(PN544_DRV_NAME ":IRQ GPIO = %d\n", irq_gpio_val); + if (irq_gpio_val == 0) { + if (filp->f_flags & O_NONBLOCK) { + pr_err(PN544_DRV_NAME ":f_falg has O_NONBLOCK. EAGAIN!\n"); + ret = -EAGAIN; + goto fail; + } + + pn544_dev->irq_enabled = true; +#ifdef LGE_NFC_READ_IRQ_MODIFY + do_reading=0;//DY_TEST +#endif +// 20120831, jh.heo@lge.com Fix to irq interrupt in sleep mode. +#if !defined(LGE_NFC_HW_QCT_MSM8660) + enable_irq_wake(pn544_get_irq_pin(pn544_dev)); +#endif + enable_irq(pn544_get_irq_pin(pn544_dev)); +#ifdef LGE_NFC_READ_IRQ_MODIFY + ret = wait_event_interruptible(pn544_dev->read_wq, do_reading); +#else + ret = wait_event_interruptible(pn544_dev->read_wq, + gpio_get_value(pn544_dev->irq_gpio)); +#endif + pn544_disable_irq(pn544_dev); + //dprintk(PN544_DRV_NAME ":wait_event_interruptible : %d\n", ret); +#ifdef LGE_NFC_READ_IRQ_MODIFY + //DY_TEST + if(cancle_read == true) + { + cancle_read = false; + ret = -1; + goto fail; + } +#endif + if (ret) + goto fail; + } + } + + /* Read data */ + memset(tmp, 0x00, MAX_BUFFER_SIZE); + ret = i2c_master_recv(pn544_dev->client, tmp, count); + mutex_unlock(&pn544_dev->read_mutex); + + if (ret < 0) { + pr_err("%s: i2c_master_recv returned %d\n", __func__, ret); + return ret; + } + if (ret > count) { + pr_err("%s: received too many bytes from i2c (%d)\n", + __func__, ret); + return -EIO; + } + if (copy_to_user(buf, tmp, ret)) { + pr_warning("%s : failed to copy to user space\n", __func__); + return -EFAULT; + } + + return ret; + +fail: + mutex_unlock(&pn544_dev->read_mutex); + return ret; +} + +static ssize_t pn544_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct pn544_dev *pn544_dev; + static char tmp[MAX_BUFFER_SIZE]; + int ret; + + pn544_dev = filp->private_data; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + memset(tmp, 0x00, MAX_BUFFER_SIZE); + if (copy_from_user(tmp, buf, count)) { + pr_err(PN544_DRV_NAME ":%s : failed to copy from user space\n", __func__); + return -EFAULT; + } + + pr_debug("%s : writing %zu bytes.\n", __func__, count); + /* Write data */ + dprintk(PN544_DRV_NAME ":write: pn544_write len=:%d\n", count); + + ret = i2c_master_send(pn544_dev->client, tmp, count); + if (ret != count) { + pr_err("%s : i2c_master_send returned %d\n", __func__, ret); + ret = -EIO; + } + + return ret; +} + +static int pn544_dev_open(struct inode *inode, struct file *filp) +{ + filp->private_data = i2c_get_clientdata(pn544_client); + pr_debug("%s : %d,%d\n", __func__, imajor(inode), iminor(inode)); + + return 0; +} + +static long pn544_dev_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct pn544_dev *pn544_dev = filp->private_data; + + switch (cmd) { + case PN544_SET_PWR: + if (arg == 2) { + /* + power on with firmware download (requires hw reset) + */ + dprintk(PN544_DRV_NAME ":%s power on with firmware\n", __func__); + + gpio_set_value(pn544_dev->ven_gpio, 1); + gpio_set_value(pn544_dev->firm_gpio, 1); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); + } else if (arg == 1) { + /* power on */ + dprintk(PN544_DRV_NAME ":%s power on\n", __func__); + + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 1); + msleep(10); +#ifdef CONFIG_LGE_NFC_SET_IRQ_WAKEUP + if (sIrqState == false) { + irq_set_irq_wake(pn544_dev->client->irq,1); + sIrqState = true; + dprintk(PN544_DRV_NAME ":%s enable IRQ\n", __func__); + } + else { + pr_err("%s IRQ is already enabled!\n", __func__); + } +#endif + } else if (arg == 0) { + /* power off */ + dprintk(PN544_DRV_NAME ":%s power off\n", __func__); + gpio_set_value(pn544_dev->firm_gpio, 0); + gpio_set_value(pn544_dev->ven_gpio, 0); + msleep(10); +#ifdef CONFIG_LGE_NFC_SET_IRQ_WAKEUP + if (sIrqState == true) { + irq_set_irq_wake(pn544_dev->client->irq,0); + sIrqState = false; + dprintk(PN544_DRV_NAME ":%s disable IRQ\n", __func__); + } + else { + pr_err("%s IRQ is already disabled!\n", __func__); + } +#endif +#ifdef LGE_NFC_READ_IRQ_MODIFY + } else if (arg == 3) {//DY_TEST + dprintk("%s Read Cancle\n", __func__); + cancle_read = true; + do_reading = 1; + wake_up(&pn544_dev->read_wq); +#endif + } else { + pr_err("%s bad arg %ld\n", __func__, arg); + return -EINVAL; + } + break; + case PN544_INTERRUPT_CMD: + { + /* + pn544_disable_irq = level; + */ + dprintk(PN544_DRV_NAME ":ioctl: pn544_interrupt enable level:%ld\n", arg); + break; + } + case PN544_READ_POLLING_CMD: + { + stReadIntFlag = arg; + dprintk(PN544_DRV_NAME ":ioctl: pn544_polling flag set:%ld\n", arg); + break; + } + case PN544_HW_REVISION: + { + return pn544_get_hw_revision(); + } + default: + pr_err("%s bad ioctl %d\n", __func__, cmd); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations pn544_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = pn544_dev_read, + .write = pn544_dev_write, + .open = pn544_dev_open, + .unlocked_ioctl = pn544_dev_unlocked_ioctl, +}; + +static int pn544_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct pn544_dev *pn544_dev = NULL; + pn544_client = client; + + dprintk(PN544_DRV_NAME ": pn544_probe() start\n"); + + pn544_dev = kzalloc(sizeof(*pn544_dev), GFP_KERNEL); + if (pn544_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + + pn544_parse_dt(&client->dev, pn544_dev); + + pn544_dev->client = client; + dprintk(PN544_DRV_NAME ":IRQ : %d\nVEN : %d\nFIRM : %d\n", + pn544_dev->irq_gpio, pn544_dev->ven_gpio, pn544_dev->firm_gpio); + + ret = gpio_request(pn544_dev->irq_gpio, "nfc_int"); + if (ret) { + dprintk(PN544_DRV_NAME ":pn544_probe() : nfc_int request failed!\n"); + goto err_int; + } + ret = gpio_request(pn544_dev->ven_gpio, "nfc_ven"); + if (ret) { + dprintk(PN544_DRV_NAME ":pn544_probe() : nfc_ven request failed!\n"); + goto err_ven; + } + ret = gpio_request(pn544_dev->firm_gpio, "nfc_firm"); + if (ret) { + dprintk(PN544_DRV_NAME ":pn544_probe() : nfc_firm request failed!\n"); + goto err_firm; + } + + pn544_gpio_enable(pn544_dev); + + ret = gpio_direction_output(pn544_dev->ven_gpio,1); + ret = gpio_direction_output(pn544_dev->firm_gpio,0); + ret = gpio_direction_input(pn544_dev->irq_gpio); + + /* init mutex and queues */ + init_waitqueue_head(&pn544_dev->read_wq); + mutex_init(&pn544_dev->read_mutex); +#ifdef CONFIG_LGE_NFC_PRESTANDBY + mutex_init(&mode_mutex); +#endif + spin_lock_init(&pn544_dev->irq_enabled_lock); + + pn544_dev->pn544_device.minor = MISC_DYNAMIC_MINOR; + pn544_dev->pn544_device.name = PN544_DRV_NAME; + pn544_dev->pn544_device.fops = &pn544_dev_fops; + + ret = misc_register(&pn544_dev->pn544_device); + if (ret) { + pr_err("%s : misc_register failed\n", __FILE__); + goto err_misc_register; + } + + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + pr_info("%s : requesting IRQ %d\n", __func__, client->irq); + pn544_dev->irq_enabled = true; + ret = request_irq(pn544_gpio_to_irq(pn544_dev), pn544_dev_irq_handler, + IRQF_TRIGGER_HIGH, client->name, pn544_dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_request_irq_failed; + } +#if !defined(LGE_NFC_HW_QCT_MSM8660)&&!defined(CONFIG_LGE_NFC_HW_QCT_MSM8255) + enable_irq_wake(pn544_get_irq_pin(pn544_dev)); +#endif + pn544_disable_irq(pn544_dev); + i2c_set_clientdata(client, pn544_dev); + dprintk(PN544_DRV_NAME ": pn544_probe() end\n"); +/* LGE_CHANGE_S + * + * do device driver initialization + * using multithread during booting, + * in order to reduce booting time. + * + * byungchul.park@lge.com 20120328 + */ +#ifdef CONFIG_LGE_NFC_PRESTANDBY + if (pn544_validate_boot_mode()) { + dprintk("%s : get in the standbyset\n", __func__); +#ifdef CONFIG_LGE_NFC_MULTICORE_FASTBOOT + { + struct task_struct *th; + th = kthread_create(pn544_factory_standby_set_thread, NULL, "pn544_factory_standby"); + if (IS_ERR(th)) { + ret = PTR_ERR(th); + goto err_request_irq_failed; + } + wake_up_process(th); + } +#else + pn544_factory_standby_set(); +#endif +/* LGE_CHANGE_E */ + } +#endif + return 0; + +err_request_irq_failed: + misc_deregister(&pn544_dev->pn544_device); + +err_misc_register: + mutex_destroy(&pn544_dev->read_mutex); +#ifdef CONFIG_LGE_NFC_PRESTANDBY + mutex_destroy(&mode_mutex); +#endif + gpio_free(pn544_dev->firm_gpio); + +err_firm: + gpio_free(pn544_dev->ven_gpio); + +err_ven: + gpio_free(pn544_dev->irq_gpio); + +err_int: + kfree(pn544_dev); + +err_exit: + pr_err(PN544_DRV_NAME ": pn544_dev is null\n"); + pr_err(PN544_DRV_NAME ": pn544_probe() end with error!\n"); + + return ret; +} + +static __devexit int pn544_remove(struct i2c_client *client) +{ + struct pn544_dev *pn544_dev; + + pn544_dev = i2c_get_clientdata(client); + free_irq(pn544_gpio_to_irq(pn544_dev), pn544_dev); + misc_deregister(&pn544_dev->pn544_device); + mutex_destroy(&pn544_dev->read_mutex); +#ifdef CONFIG_LGE_NFC_PRESTANDBY + mutex_destroy(&mode_mutex); +#endif + gpio_free(pn544_dev->firm_gpio); + gpio_free(pn544_dev->ven_gpio); + gpio_free(pn544_dev->irq_gpio); + kfree(pn544_dev); + + return 0; +} + +static void pn544_shutdown(struct i2c_client *client) +{ + struct pn544_dev *pn544_dev; + // Get PN544 Device Structure data + pn544_dev = i2c_get_clientdata(client); + + pn544_shutdown_cb(pn544_dev); + return; +} + +static const struct i2c_device_id pn544_id[] = { + { PN544_DRV_NAME, 0 }, + { } +}; + +#ifdef CONFIG_LGE_NFC_PN547 +static struct of_device_id pn547_match_table[] = { + { .compatible = "nxp,pn547",}, + { }, +}; + +static struct i2c_driver pn544_driver = { + .driver = { + .owner = THIS_MODULE, + .name = PN544_DRV_NAME, + .of_match_table = pn547_match_table, + }, + .probe = pn544_probe, + .remove = __devexit_p(pn544_remove), + .shutdown = pn544_shutdown, + .id_table = pn544_id, +}; +#else +static struct of_device_id pn544_match_table[] = { + { .compatible = "nxp,pn544",}, + { }, +}; + +static struct i2c_driver pn544_driver = { + .driver = { + .owner = THIS_MODULE, + .name = PN544_DRV_NAME, + .of_match_table = pn544_match_table, + }, + .probe = pn544_probe, + .remove = __devexit_p(pn544_remove), + .shutdown = pn544_shutdown, + .id_table = pn544_id, +}; +#endif + +/* + * module load/unload record keeping + */ + +static int __init pn544_dev_init(void) +{ + int ret = 0; + + pr_info("Loading pn544 driver\n"); + + ret = i2c_add_driver(&pn544_driver); + if (ret < 0) { + printk("[NFC]failed to i2c_add_driver\n"); + } + pr_info("Loading pn544 or pn547 driver Success! \n"); + return ret; + +} +module_init(pn544_dev_init); + +static void __exit pn544_dev_exit(void) +{ + pr_info("Unloading pn544 driver\n"); + i2c_del_driver(&pn544_driver); +} +module_exit(pn544_dev_exit); + +/* LGE_CHANGE_S, [NFC][minwoo.kwon@lge.com], 2013-03-07, NFC Bring up */ +MODULE_DEVICE_TABLE(i2c, pn544_id); +/* LGE_CHANGE_E, [NFC][minwoo.kwon@lge.com], 2013-03-07, NFC Bring up */ +MODULE_AUTHOR("Sylvain Fonteneau"); +MODULE_DESCRIPTION("NFC PN544 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/pn544_lge_hwadapter.c b/drivers/nfc/pn544_lge_hwadapter.c new file mode 100644 index 000000000000..fcf2fd9d8cfb --- /dev/null +++ b/drivers/nfc/pn544_lge_hwadapter.c @@ -0,0 +1,80 @@ + +#include "pn544_lge_hwadapter.h" + +bool pn544_validate_boot_mode(void) { + enum lge_boot_mode_type boot_mode; + boot_mode = lge_get_boot_mode(); + printk("pn544_probe() boot_mode : %d\n",boot_mode); + if (boot_mode == LGE_BOOT_MODE_NORMAL) { + printk("boot_mode :LGE_BOOT_MODE_NORMAL\n"); + return false; + } + return true; +} + +int pn544_get_hw_revision(void) +{ +#if defined(CONFIG_LGE_NFC_HW_TI_OMAP4430) + int hw_revision = LGE_PCB_MAX; + hw_revision = system_rev; +#else + hw_rev_type hw_revision = HW_REV_MAX; + hw_revision = lge_get_board_revno(); +#endif + dprintk(PN544_DRV_NAME ":ioctl: pn544_read hw revision : %d\n", hw_revision); + return (int)hw_revision; +} + +unsigned int pn544_get_irq_pin(struct pn544_dev *dev) +{ +#if defined(CONFIG_LGE_NFC_HW_QCT_APQ8064)||defined(CONFIG_LGE_NFC_HW_QCT_MSM8255) + return dev->client->irq; +#elif defined(CONFIG_LGE_NFC_HW_TI_OMAP4430) + return OMAP_GPIO_IRQ(dev->irq_gpio); +#elif defined(CONFIG_LGE_NFC_HW_NV_AP3X) + return dev->client->irq; +#else + return dev->client->irq; +#endif +} + +int pn544_gpio_to_irq(struct pn544_dev *dev) +{ +#if defined(CONFIG_LGE_NFC_HW_TI_OMAP4430)||defined(CONFIG_LGE_NFC_HW_QCT_MSM8255) + return gpio_to_irq(dev->irq_gpio); +#else + return dev->client->irq; +#endif +} + +void pn544_gpio_enable(struct pn544_dev *pn544_dev) +{ +#if defined(CONFIG_LGE_NFC_HW_NV_AP3X) + tegra_gpio_enable(pn544_dev->ven_gpio); + tegra_gpio_enable(pn544_dev->firm_gpio); + tegra_gpio_enable(pn544_dev->irq_gpio); +#endif + return; +} + +void pn544_shutdown_cb(struct pn544_dev *pn544_dev) +{ +#if defined(CONFIG_LGE_NFC_HW_QCT_MSM8660) + dprintk("================ pn544_shutdown() start ================\n"); + + // Make all output GPIOs to Low + gpio_set_value(pn544_dev->ven_gpio, 0); + gpio_set_value(pn544_dev->firm_gpio, 0); + msleep(10); + dprintk("Output GPIO Status : VEN = %d, FIRM = %d\n", + gpio_get_value(pn544_dev->ven_gpio), + gpio_get_value(pn544_dev->firm_gpio)); + + dprintk("================ pn544_shutdown() end ================\n"); + +#elif defined(CONFIG_LGE_NFC_HW_NV_AP3X) + gpio_set_value(pn544_dev->ven_gpio, 0); +#endif + return; +} + diff --git a/drivers/nfc/pn544_lge_hwadapter.h b/drivers/nfc/pn544_lge_hwadapter.h new file mode 100644 index 000000000000..1a9ac615abf3 --- /dev/null +++ b/drivers/nfc/pn544_lge_hwadapter.h @@ -0,0 +1,16 @@ +#ifndef _PN544_LGE_HWADAPTER_H_ +#define _PN544_LGE_HWADAPTER_H_ + +#include + +#include + +bool pn544_validate_boot_mode(void); +int pn544_get_hw_revision(void); +unsigned int pn544_get_irq_pin(struct pn544_dev *dev); +int pn544_gpio_to_irq(struct pn544_dev *dev); +void pn544_gpio_enable(struct pn544_dev *pn544_dev); + +void pn544_shutdown_cb(struct pn544_dev *pn544_dev); + +#endif /* _PN544_LGE_HWADAPTER_H_ */ diff --git a/include/linux/nfc/pn544_lge.h b/include/linux/nfc/pn544_lge.h new file mode 100644 index 000000000000..53f545095571 --- /dev/null +++ b/include/linux/nfc/pn544_lge.h @@ -0,0 +1,101 @@ +/* lge/include/nfc_nxp_pn544pn65n.h + * + * Copyright (C) 2010 NXP Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _PN544_LGE_H_ +#define _PN544_LGE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PN544_MAGIC 0xE9 + + +#ifdef CONFIG_LGE_NFC_PN547 +#define PN544_DRV_NAME "pn547" //garam for pn547 +#else +#define PN544_DRV_NAME "pn544" //garam for pn547 +#endif + +/* + * PN544 power control via ioctl + * PN544_SET_PWR(0): power off + * PN544_SET_PWR(1): power on + * PN544_SET_PWR(2): reset and power on with firmware download enabled + */ +#define PN544_SET_PWR _IOW(PN544_MAGIC, 0x01, unsigned int) + +#define PN544_HW_REVISION _IOR(PN544_MAGIC, 0x02, unsigned int) + +struct pn544_i2c_platform_data { + unsigned int sda_gpio; + unsigned int scl_gpio; + unsigned int irq_gpio; + unsigned int ven_gpio; + unsigned int firm_gpio; +}; + + +struct pn544_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct i2c_client *client; + struct miscdevice pn544_device; + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int irq_gpio; + bool irq_enabled; + spinlock_t irq_enabled_lock; +}; + +struct pn544_gpio { + unsigned int sda_gpio; // byunggu + unsigned int scl_gpio; // byunggu + unsigned int ven_gpio; + unsigned int firm_gpio; + unsigned int irq_gpio; +}; + +#define LGE_NFC_READ_IRQ_MODIFY//DY_TEST + +/* seokmin added for debugging */ +#define PN544_INTERRUPT_CMD 2 +#define PN544_READ_POLLING_CMD 3 + + +#define dprintk(fmt, args...) printk(fmt, ##args) + + +#endif /* */ + From 952dd7aec53bba32479f505c43f3e3fac96b74cb Mon Sep 17 00:00:00 2001 From: vm03 Date: Mon, 22 Dec 2014 10:24:00 +0300 Subject: [PATCH 041/104] use w5 for w5 and w5ds --- .../msm8610-w5-input.dtsi | 47 ++++++++----------- arch/arm/configs/w5_global_com_defconfig | 1 + 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi index de6d3483f98d..1e047ad6d6b0 100644 --- a/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi +++ b/arch/arm/boot/dts/msm8610-w5_global_com/msm8610-w5-input.dtsi @@ -13,59 +13,50 @@ &soc { i2c@f9923000{ mms100s@48 { - melfas,product = "I2S45B"; + melfas,product = "I4S45A"; melfas,max-x = <480>; melfas,max-y = <800>; melfas,use_vdd_i2c = <1>; melfas,gpio-vdd-en = <62>; - melfas,key-map = <158 139>; - melfas,fw-image-name = "melfas/w5_global_com/w5_global_com_rev_b_v101.mfsb"; + melfas,key-map = <158 172 139 249>; + melfas,fw-image-name = "melfas/w5ds_global_com/w5ds_global_com_rev_b_v1.04.mfsb"; + revision = "rev_a...rev_b"; status = "ok"; - revision = "rev_b"; }; - mms100s@48_rev_c { - melfas,product = "I2S45B"; + melfas,product = "I4S45A"; melfas,max-x = <480>; melfas,max-y = <800>; melfas,use_vdd_i2c = <1>; melfas,gpio-vdd-en = <82>; - melfas,key-map = <158 139>; - melfas,fw-image-name = "melfas/w5_global_com/w5_global_com_rev_b_v101.mfsb"; + melfas,key-map = <158 172 139 249>; + melfas,fw-image-name = "melfas/w5ds_global_com/w5ds_global_com_rev_b_v1.04.mfsb"; + revision = "rev_c"; status = "ok"; - revision = "rev_c"; }; - - lgd_melfas@34 { - status = "ok"; - revision = "rev_a"; - }; - - ads_ags04@6a { - status = "ok"; - revision = "rev_a"; - }; - synaptics_red@20 { status = "disable"; revision = "rev_d..."; - synaptics,button-map = <158 139>; + synaptics,button-map = <158 172 139 249>; + synaptics,i2c-pull-up = <0>; + synaptics,power-down = <1>; synaptics,gpio_vdd_en = <82>; }; synaptics_s220x@20 { status = "ok"; revision = "rev_d..."; - synaptics,fw_version_info = <0x82 0x04 0x51>; - synaptics,fw_image = "synaptics/w5_global_com/PLG389-V1.10-PR1600582-DS4.3.5.1.16_1204518A.img", /* panel id 0 */ - "synaptics/w5_global_com/PLG317-V1.14-PR1600582-DS4.3.5.1.16_3204518E.img"; /* panel id 1 */ - synaptics,panel_spec = "synaptics/w5_global_com/w5_Suntel_limit.txt", /* panel id 0 */ - "synaptics/w5_global_com/w5_Inotek_limit.txt";/* panel id 1 */ + synaptics,fw_version_info = <0x84 0x04 0x51>; + synaptics,fw_image = "synaptics/w5ds_global_com/PLG388-V1.10-PR1600582-DS4.3.5.1.16_1404518A.img", /*panel id 0*/ + "synaptics/w5ds_global_com/PLG316-V1.13-PR1600582-DS4.3.5.1.16_3404518D.img"; /*panel id 1*/ + synaptics,panel_spec = "synaptics/w5ds_global_com/w5ds_Suntel_limit.txt", /*panel id 0*/ + "synaptics/w5ds_global_com/w5ds_Inotek_limit.txt";/*panel id 1*/ synaptics,global_access_pixel = <10>; lge,knock_on_type = <1>; + synaptics,platform_data { - number_of_button = <2>; - button_name = [9e 8b]; + number_of_button = <4>; + button_name = [9e ac 8b f9]; x_max = <960>; y_max = <1600>; lcd_x = <480>; diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig index 658167157081..9bbf9bd2090f 100644 --- a/arch/arm/configs/w5_global_com_defconfig +++ b/arch/arm/configs/w5_global_com_defconfig @@ -418,6 +418,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_LCD_KCAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y From aa5d15f18b6fdfafc08cf6e86b5aa4ec5f550b28 Mon Sep 17 00:00:00 2001 From: savoca Date: Thu, 20 Nov 2014 01:46:55 -0500 Subject: [PATCH 042/104] LCD_KCAL: Color control driver for msm8x10/mdp3 Major thanks to @faux123 and LGE for previous implementations and reference. --- arch/arm/configs/w3ds_global_com_defconfig | 1 + arch/arm/configs/w5ds_global_com_defconfig | 1 + drivers/video/msm/mdss/Kconfig | 5 + drivers/video/msm/mdss/mdp3.h | 18 ++ drivers/video/msm/mdss/mdp3_ctrl.c | 196 +++++++++++++++++++++ drivers/video/msm/mdss/mdp3_dma.c | 88 +++++++++ drivers/video/msm/mdss/mdss_fb.c | 4 + drivers/video/msm/mdss/mdss_fb.h | 5 + 8 files changed, 318 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index 4765597bd7a2..3f4ba8114905 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -413,6 +413,7 @@ CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MIPI_TIANMA_CMD_HVGA_PT_PANEL=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_LCD_KCAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index 0b350cebf975..f7acfba64e26 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -413,6 +413,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_LCD_KCAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/drivers/video/msm/mdss/Kconfig b/drivers/video/msm/mdss/Kconfig index 01edf9201c5b..c84795755fb2 100644 --- a/drivers/video/msm/mdss/Kconfig +++ b/drivers/video/msm/mdss/Kconfig @@ -35,3 +35,8 @@ config FB_MSM_MDSS_MDP3 ---help--- The MDP3 provides support for an older version display controller included in latest display sub-system, known as MDSS. + +config LCD_KCAL + depends on FB_MSM_MDSS_MDP3 + bool "Color control driver for MSM_MDSS_MDP3 devices" + default n diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h index a253d8f4589b..3ec3f1f927a2 100644 --- a/drivers/video/msm/mdss/mdp3.h +++ b/drivers/video/msm/mdss/mdp3.h @@ -207,4 +207,22 @@ int mdp3_misr_get(struct mdp_misr *misr_resp); #define MDP3_REG_WRITE(addr, val) writel_relaxed(val, mdp3_res->mdp_base + addr) #define MDP3_REG_READ(addr) readl_relaxed(mdp3_res->mdp_base + addr) +#ifdef CONFIG_LCD_KCAL +#define R_MASK 0x00ff0000 +#define G_MASK 0x000000ff +#define B_MASK 0x0000ff00 +#define R_SHIFT 16 +#define G_SHIFT 0 +#define B_SHIFT 8 +#define lut2r(lut) ((lut & R_MASK) >> R_SHIFT) +#define lut2g(lut) ((lut & G_MASK) >> G_SHIFT) +#define lut2b(lut) ((lut & B_MASK) >> B_SHIFT) + +#define NUM_QLUT 256 +#define MAX_KCAL_V (NUM_QLUT-1) +#define scaled_by_kcal(rgb, kcal) \ + (((((unsigned int)(rgb) * (unsigned int)(kcal)) << 16) / \ + (unsigned int)MAX_KCAL_V) >> 16) +#endif + #endif /* MDP3_H */ diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c index 763c164871bd..694ea08daa03 100644 --- a/drivers/video/msm/mdss/mdp3_ctrl.c +++ b/drivers/video/msm/mdss/mdp3_ctrl.c @@ -1579,8 +1579,13 @@ static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd, return ret; } +#ifdef CONFIG_LCD_KCAL +static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, + struct fb_cmap *cmap, bool setup_hw) +#else static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, struct fb_cmap *cmap) +#endif { int rc = 0; struct mdp3_session_data *mdp3_session = mfd->mdp.private1; @@ -1599,6 +1604,9 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, return -EINVAL; } +#ifdef CONFIG_LCD_KCAL + if (setup_hw) { +#endif rc = copy_from_user(r + cmap->start, cmap->red, sizeof(u16)*cmap->len); rc |= copy_from_user(g + cmap->start, @@ -1607,14 +1615,27 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, cmap->blue, sizeof(u16)*cmap->len); if (rc) return rc; +#ifdef CONFIG_LCD_KCAL + } +#endif lut_config.lut_enable = 7; lut_config.lut_sel = mdp3_session->lut_sel; lut_config.lut_position = 0; lut_config.lut_dirty = true; +#ifdef CONFIG_LCD_KCAL + if (setup_hw) { +#endif lut.color0_lut = r; lut.color1_lut = g; lut.color2_lut = b; +#ifdef CONFIG_LCD_KCAL + } else { + lut.color0_lut = cmap->red; + lut.color1_lut = cmap->green; + lut.color2_lut = cmap->blue; + } +#endif mutex_lock(&mdp3_session->lock); @@ -1674,6 +1695,170 @@ static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd, return rc; } +#ifdef CONFIG_LCD_KCAL +static int update_preset_lcdc_lut(struct msm_fb_data_type *mfd, + bool setup_hw, int red, int green, int blue) +{ + struct fb_cmap cmap; + int ret = 0; + + cmap.start = 0; + cmap.len = 256; + cmap.transp = NULL; + + cmap.red = (uint16_t *)&(red); + cmap.green = (uint16_t *)&(green); + cmap.blue = (uint16_t *)&(blue); + + ret = mdp3_ctrl_lut_update(mfd, &cmap, setup_hw); + if (ret) + pr_err("%s: failed to set lut! %d\n", __func__, ret); + + return ret; +} + +struct kcal_data { + int r; + int g; + int b; + int min; +}; + +static struct kcal_data lut = {255, 255, 255, 35}; + +static int kcal_set_values(struct msm_fb_data_type *mfd, + int kcal_r, int kcal_g, int kcal_b) +{ + lut.r = kcal_r < lut.min ? lut.min : kcal_r; + lut.g = kcal_g < lut.min ? lut.min : kcal_g; + lut.b = kcal_b < lut.min ? lut.min : kcal_b; + + if (kcal_r < lut.min || kcal_g < lut.min || kcal_b < lut.min) + update_preset_lcdc_lut(mfd, false, lut.r, lut.g, lut.b); + + return 0; +} + +static int kcal_get_values(int *kcal_r, int *kcal_g, int *kcal_b) +{ + *kcal_r = lut.r; + *kcal_g = lut.g; + *kcal_b = lut.b; + return 0; +} + +static int kcal_set_min(struct msm_fb_data_type *mfd, int kcal_min) +{ + lut.min = kcal_min; + + if (lut.min > lut.r || lut.min > lut.g || lut.min > lut.b) { + lut.r = lut.r < lut.min ? lut.min : lut.r; + lut.g = lut.g < lut.min ? lut.min : lut.g; + lut.b = lut.b < lut.min ? lut.min : lut.b; + update_preset_lcdc_lut(mfd, false, lut.r, lut.g, lut.b); + } + + return 0; +} + +static int kcal_get_min(int *kcal_min) +{ + *kcal_min = lut.min; + return 0; +} + +static int kcal_refresh_display(struct msm_fb_data_type *mfd) +{ + return update_preset_lcdc_lut(mfd, false, lut.r, lut.g, lut.b); +} + +static ssize_t kcal_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + int kcal_r = 0; + int kcal_g = 0; + int kcal_b = 0; + + if (!count) + return -EINVAL; + + sscanf(buf, "%d %d %d", &kcal_r, &kcal_g, &kcal_b); + + if (kcal_r < 0 || kcal_r > 255) + return -EINVAL; + + if (kcal_g < 0 || kcal_g > 255) + return -EINVAL; + + if (kcal_b < 0 || kcal_b > 255) + return -EINVAL; + + kcal_set_values(mfd, kcal_r, kcal_g, kcal_b); + kcal_refresh_display(mfd); + + return count; +} + +static ssize_t kcal_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int kcal_r = 0; + int kcal_g = 0; + int kcal_b = 0; + + kcal_get_values(&kcal_r, &kcal_g, &kcal_b); + + return sprintf(buf, "%d %d %d\n", kcal_r, kcal_g, kcal_b); +} + +static ssize_t kcal_min_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + int kcal_min = 0; + + if (!count) + return -EINVAL; + + sscanf(buf, "%d", &kcal_min); + + if (kcal_min < 0 || kcal_min > 255) + return -EINVAL; + + kcal_set_min(mfd, kcal_min); + kcal_refresh_display(mfd); + + return count; +} + +static ssize_t kcal_min_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int kcal_min = 0; + + kcal_get_min(&kcal_min); + return sprintf(buf, "%d\n", kcal_min); +} + +static DEVICE_ATTR(kcal, 0644, kcal_show, kcal_store); +static DEVICE_ATTR(kcal_min, 0644, kcal_min_show, kcal_min_store); + +static struct attribute *kcal_fs_attrs[] = { + &dev_attr_kcal.attr, + &dev_attr_kcal_min.attr, + NULL, +}; + +static struct attribute_group kcal_fs_attr_group = { + .attrs = kcal_fs_attrs, +}; +#endif + static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void __user *argp) { @@ -1898,6 +2083,14 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) kobject_uevent(&dev->kobj, KOBJ_ADD); pr_debug("vsync kobject_uevent(KOBJ_ADD)\n"); +#ifdef CONFIG_LCD_KCAL + rc = sysfs_create_group(&dev->kobj, &kcal_fs_attr_group); + if (rc) { + pr_err("kcal sysfs group creation failed, ret=%d\n", rc); + goto init_done; + } +#endif + if (mdp3_get_cont_spash_en()) { mdp3_session->clk_on = 1; mdp3_ctrl_notifier_register(mdp3_session, @@ -1910,6 +2103,9 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) } mdp3_session->vsync_before_commit = true; +#ifdef CONFIG_LCD_KCAL + update_preset_lcdc_lut(mfd, true, 255, 255, 255); +#endif init_done: if (IS_ERR_VALUE(rc)) kfree(mdp3_session); diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c index da08246660ff..3f5491e47424 100644 --- a/drivers/video/msm/mdss/mdp3_dma.c +++ b/drivers/video/msm/mdss/mdp3_dma.c @@ -526,12 +526,86 @@ static int mdp3_dmap_ccs_config(struct mdp3_dma *dma, return 0; } +#ifdef CONFIG_LCD_KCAL +static unsigned int lcd_rgb_working_lut[256] = { + /* default linear qlut */ + 0x00000000, 0x00010101, 0x00020202, 0x00030303, + 0x00040404, 0x00050505, 0x00060606, 0x00070707, + 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b, + 0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, + 0x00101010, 0x00111111, 0x00121212, 0x00131313, + 0x00141414, 0x00151515, 0x00161616, 0x00171717, + 0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, + 0x001c1c1c, 0x001d1d1d, 0x001e1e1e, 0x001f1f1f, + 0x00202020, 0x00212121, 0x00222222, 0x00232323, + 0x00242424, 0x00252525, 0x00262626, 0x00272727, + 0x00282828, 0x00292929, 0x002a2a2a, 0x002b2b2b, + 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f, + 0x00303030, 0x00313131, 0x00323232, 0x00333333, + 0x00343434, 0x00353535, 0x00363636, 0x00373737, + 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b, + 0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, + 0x00404040, 0x00414141, 0x00424242, 0x00434343, + 0x00444444, 0x00454545, 0x00464646, 0x00474747, + 0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, + 0x004c4c4c, 0x004d4d4d, 0x004e4e4e, 0x004f4f4f, + 0x00505050, 0x00515151, 0x00525252, 0x00535353, + 0x00545454, 0x00555555, 0x00565656, 0x00575757, + 0x00585858, 0x00595959, 0x005a5a5a, 0x005b5b5b, + 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f, + 0x00606060, 0x00616161, 0x00626262, 0x00636363, + 0x00646464, 0x00656565, 0x00666666, 0x00676767, + 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b, + 0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, + 0x00707070, 0x00717171, 0x00727272, 0x00737373, + 0x00747474, 0x00757575, 0x00767676, 0x00777777, + 0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, + 0x007c7c7c, 0x007d7d7d, 0x007e7e7e, 0x007f7f7f, + 0x00808080, 0x00818181, 0x00828282, 0x00838383, + 0x00848484, 0x00858585, 0x00868686, 0x00878787, + 0x00888888, 0x00898989, 0x008a8a8a, 0x008b8b8b, + 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f, + 0x00909090, 0x00919191, 0x00929292, 0x00939393, + 0x00949494, 0x00959595, 0x00969696, 0x00979797, + 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b, + 0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, + 0x00a0a0a0, 0x00a1a1a1, 0x00a2a2a2, 0x00a3a3a3, + 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7, + 0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, + 0x00acacac, 0x00adadad, 0x00aeaeae, 0x00afafaf, + 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3, + 0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, + 0x00b8b8b8, 0x00b9b9b9, 0x00bababa, 0x00bbbbbb, + 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf, + 0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, + 0x00c4c4c4, 0x00c5c5c5, 0x00c6c6c6, 0x00c7c7c7, + 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb, + 0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, + 0x00d0d0d0, 0x00d1d1d1, 0x00d2d2d2, 0x00d3d3d3, + 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7, + 0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, + 0x00dcdcdc, 0x00dddddd, 0x00dedede, 0x00dfdfdf, + 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3, + 0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, + 0x00e8e8e8, 0x00e9e9e9, 0x00eaeaea, 0x00ebebeb, + 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef, + 0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, + 0x00f4f4f4, 0x00f5f5f5, 0x00f6f6f6, 0x00f7f7f7, + 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb, + 0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff +}; +#endif + static int mdp3_dmap_lut_config(struct mdp3_dma *dma, struct mdp3_dma_lut_config *config, struct mdp3_dma_lut *lut) { u32 addr, color; int i; +#ifdef CONFIG_LCD_KCAL + u16 r, g, b; + uint32_t *internal_lut = lcd_rgb_working_lut; +#endif if (config->lut_enable && lut) { addr = MDP3_REG_DMA_P_CSC_LUT1; @@ -539,9 +613,23 @@ static int mdp3_dmap_lut_config(struct mdp3_dma *dma, addr = MDP3_REG_DMA_P_CSC_LUT2; for (i = 0; i < MDP_LUT_SIZE; i++) { +#ifdef CONFIG_LCD_KCAL + r = lut2r(internal_lut[i]); + g = lut2g(internal_lut[i]); + b = lut2b(internal_lut[i]); + + r = scaled_by_kcal(r, *(lut->color0_lut)); + g = scaled_by_kcal(g, *(lut->color1_lut)); + b = scaled_by_kcal(b, *(lut->color2_lut)); + + color = g & 0xff; + color |= (r & 0xff) << 8; + color |= (b & 0xff) << 16; +#else color = lut->color0_lut[i] & 0xff; color |= (lut->color1_lut[i] & 0xff) << 8; color |= (lut->color2_lut[i] & 0xff) << 16; +#endif MDP3_REG_WRITE(addr, color); addr += 4; } diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c index c7d61d3a4167..bd02c7fef8b6 100644 --- a/drivers/video/msm/mdss/mdss_fb.c +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -2158,7 +2158,11 @@ static int mdss_fb_set_lut(struct fb_info *info, void __user *p) if (ret) return ret; +#ifdef CONFIG_LCD_KCAL + mfd->mdp.lut_update(mfd, &cmap, false); +#else mfd->mdp.lut_update(mfd, &cmap); +#endif return 0; } diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h index f113186a8f5f..ba2743acc5a3 100644 --- a/drivers/video/msm/mdss/mdss_fb.h +++ b/drivers/video/msm/mdss/mdss_fb.h @@ -125,7 +125,12 @@ struct msm_mdp_interface { int image_len, int *pipe_ndx); int (*cursor_update)(struct msm_fb_data_type *mfd, struct fb_cursor *cursor); +#ifdef CONFIG_LCD_KCAL + int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap, + bool setup_hw); +#else int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap); +#endif int (*do_histogram)(struct msm_fb_data_type *mfd, struct mdp_histogram *hist); int (*update_ad_input)(struct msm_fb_data_type *mfd); From 8fb7ee48d9fb60b1d0639c541db4fd81a72fd891 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 26 Nov 2014 00:14:57 +0300 Subject: [PATCH 043/104] disable orignal LCD_KCAL --- arch/arm/mach-msm/lge/devices_lge.c | 2 +- drivers/video/msm/mdss/mdss_mdp_pp.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/lge/devices_lge.c b/arch/arm/mach-msm/lge/devices_lge.c index 413315bb6591..53cf41c888d8 100644 --- a/arch/arm/mach-msm/lge/devices_lge.c +++ b/arch/arm/mach-msm/lge/devices_lge.c @@ -634,7 +634,7 @@ hw_rev_type lge_get_board_revno(void) return lge_bd_rev; } -#if defined(CONFIG_LCD_KCAL) +#if 0 //defined(CONFIG_LCD_KCAL) int g_kcal_r = 255; int g_kcal_g = 255; int g_kcal_b = 255; diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c index 2d8fa1fe0b99..0f43b0c5c379 100644 --- a/drivers/video/msm/mdss/mdss_mdp_pp.c +++ b/drivers/video/msm/mdss/mdss_mdp_pp.c @@ -21,7 +21,7 @@ #include #include -#if defined(CONFIG_LCD_KCAL) +#if 0 //defined(CONFIG_LCD_KCAL) #include extern int g_kcal_r; extern int g_kcal_g; @@ -1771,7 +1771,7 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) mdss_pp_res->gamut_disp_cfg[disp_num].flags |= MDP_PP_OPS_WRITE; } -#if defined(CONFIG_LCD_KCAL) +#if 0 //defined(CONFIG_LCD_KCAL) if (disp_num == 0) pp_sts.pgc_sts |= PP_STS_ENABLE; #endif @@ -1787,7 +1787,7 @@ int mdss_mdp_pp_resume(struct mdss_mdp_ctl *ctl, u32 dspp_num) return 0; } -#if defined(CONFIG_LCD_KCAL) +#if 0 //defined(CONFIG_LCD_KCAL) static struct mdp_ar_gc_lut_data test_r[GC_LUT_SEGMENTS] = { {0x00000000, 0x00000000, 0x00000000}, @@ -1980,7 +1980,7 @@ int mdss_mdp_pp_init(struct device *dev) } } -#if defined(CONFIG_LCD_KCAL) +#if 0 //defined(CONFIG_LCD_KCAL) if (!ret) { mdss_mdp_pp_argc(); update_preset_lcdc_lut(); From b3b9901d6714f695a3d7fab33a0150bb7139b07d Mon Sep 17 00:00:00 2001 From: vm03 Date: Sat, 10 Jan 2015 20:50:38 +0300 Subject: [PATCH 044/104] add nfc dt --- .../msm8610-w5-nfc.dtsi | 51 +++++++++++++++++++ .../msm8610-w5ds_global_com/msm8610-w5ds.dtsi | 1 + 2 files changed, 52 insertions(+) create mode 100644 arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5-nfc.dtsi diff --git a/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5-nfc.dtsi b/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5-nfc.dtsi new file mode 100644 index 000000000000..5af622b8f6e7 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5-nfc.dtsi @@ -0,0 +1,51 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* LGE_CHANGE, [NFC][garam.kim@lge.com], NFC Bring up temp */ +&soc { + + i2c@0 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 4 0x00>; + nxp,gpio_scl = <&msmgpio 5 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_b"; + }; + }; +/* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up temp */ + i2c@f9926000 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 88 0x00>; + nxp,gpio_scl = <&msmgpio 89 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_c..."; + }; + }; + + +}; diff --git a/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5ds.dtsi b/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5ds.dtsi index 9e4e9e943153..2e76c961791b 100644 --- a/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5ds.dtsi +++ b/arch/arm/boot/dts/msm8610-w5ds_global_com/msm8610-w5ds.dtsi @@ -18,6 +18,7 @@ /include/ "msm8610-w5ds-misc.dtsi" /include/ "msm8610-w5ds-pm.dtsi" /include/ "msm8610-w5ds-camera.dtsi" +/include/ "msm8610-w5-nfc.dtsi" /* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up */ &soc { serial@f991f000 { From 11f749fb578945dac836da16fe8eedcd6d6fcd31 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sat, 10 Jan 2015 20:52:42 +0300 Subject: [PATCH 045/104] fix device tree build --- arch/arm/mach-msm/Makefile.boot | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 78df289630b6..45dba47bf000 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -144,6 +144,8 @@ initrd_phys-$(CONFIG_ARCH_FSM9XXX) := 0x12000000 # MSM8610 zreladdr-$(CONFIG_ARCH_MSM8610) := 0x00008000 +ifeq ($(CONFIG_MACH_LGE),y) +else # not CONFIG_MACH_LGE dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-cdp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-cdp.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-mtp.dtb @@ -154,6 +156,7 @@ initrd_phys-$(CONFIG_ARCH_FSM9XXX) := 0x12000000 dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v1-qrd-skuab.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-qrd-skuaa.dtb dtb-$(CONFIG_ARCH_MSM8610) += msm8610-v2-qrd-skuab.dtb +endif # not CONFIG_MACH_LGE # MSMSAMARIUM zreladdr-$(CONFIG_ARCH_MSMSAMARIUM) := 0x00008000 From 4f79367237b03b29e5fe3af9ee0da32c76e40746 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 18 Jan 2015 18:53:15 +0300 Subject: [PATCH 046/104] BU52031NVX: Add lid events to support the smartcover --- drivers/misc/pm8xxx-cradle.c | 46 ++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/drivers/misc/pm8xxx-cradle.c b/drivers/misc/pm8xxx-cradle.c index 32f42317085d..74164773fad6 100755 --- a/drivers/misc/pm8xxx-cradle.c +++ b/drivers/misc/pm8xxx-cradle.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,8 @@ struct pm8xxx_cradle { static struct workqueue_struct *cradle_wq; static struct pm8xxx_cradle *cradle; +static struct input_dev *cradle_input; + static void boot_cradle_det_func(void) { int state; @@ -53,6 +56,11 @@ static void boot_cradle_det_func(void) cradle->state = state; wake_lock_timeout(&cradle->wake_lock, msecs_to_jiffies(3000)); switch_set_state(&cradle->sdev, cradle->state); + + input_report_switch(cradle_input, SW_LID, + cradle->state == SMARTCOVER_POUCH_OPENED ? 0 : 1); + input_sync(cradle_input); + } static void pm8xxx_pouch_work_func(struct work_struct *work) @@ -78,6 +86,9 @@ static void pm8xxx_pouch_work_func(struct work_struct *work) wake_lock_timeout(&cradle->wake_lock, msecs_to_jiffies(3000)); switch_set_state(&cradle->sdev, cradle->state); printk("%s : [Cradle] pouch value is %d\n", __func__ , state); + input_report_switch(cradle_input, SW_LID, + cradle->state == SMARTCOVER_POUCH_OPENED ? 0 : 1); + input_sync(cradle_input); } else { spin_unlock_irqrestore(&cradle->lock, flags); @@ -350,10 +361,40 @@ static struct platform_driver pm8xxx_cradle_driver = { }, }; +static int cradle_input_device_create(void){ + int err = 0; + + cradle_input = input_allocate_device(); + if (!cradle_input) { + err = -ENOMEM; + goto exit; + } + + cradle_input->name = "smartcover"; + cradle_input->phys = "/dev/input/smartcover"; + + set_bit(EV_SW, cradle_input->evbit); + set_bit(SW_LID, cradle_input->swbit); + + err = input_register_device(cradle_input); + if (err) { + goto exit_free; + } + return 0; + +exit_free: + input_free_device(cradle_input); + cradle_input = NULL; +exit: + return err; + +} + static int __init pm8xxx_cradle_init(void) { - cradle_wq = create_singlethread_workqueue("cradle_wq"); - printk(KERN_ERR "cradle init \n"); + cradle_input_device_create(); + cradle_wq = create_singlethread_workqueue("cradle_wq"); + printk(KERN_ERR "cradle init \n"); if (!cradle_wq) return -ENOMEM; return platform_driver_register(&pm8xxx_cradle_driver); @@ -364,6 +405,7 @@ static void __exit pm8xxx_cradle_exit(void) { if (cradle_wq) destroy_workqueue(cradle_wq); + input_unregister_device(cradle_input); platform_driver_unregister(&pm8xxx_cradle_driver); } module_exit(pm8xxx_cradle_exit); From a0a37d8f777834f9cebaba02ece3a47be2aba75f Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 15 Feb 2015 20:46:39 +0300 Subject: [PATCH 047/104] DoubleTap 2 wake with KeyHandler --- drivers/input/touchscreen/lge_touch_core.c | 6 +++--- include/linux/input.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/lge_touch_core.c b/drivers/input/touchscreen/lge_touch_core.c index df53e22ad35f..7881dde849f0 100644 --- a/drivers/input/touchscreen/lge_touch_core.c +++ b/drivers/input/touchscreen/lge_touch_core.c @@ -3138,8 +3138,8 @@ void send_uevent_lpwg(struct i2c_client* client, int type) atomic_set(&ts->state.uevent_state, UEVENT_BUSY); send_uevent(lpwg_uevent[type-1]); if (type == LPWG_DOUBLE_TAP) { - input_report_key(ts->input_dev, KEY_POWER, BUTTON_PRESSED); - input_report_key(ts->input_dev, KEY_POWER, BUTTON_RELEASED); + input_report_key(ts->input_dev, KEY_DOUBLE_TAP, BUTTON_PRESSED); + input_report_key(ts->input_dev, KEY_DOUBLE_TAP, BUTTON_RELEASED); input_sync(ts->input_dev); } } @@ -4221,7 +4221,7 @@ static int touch_probe(struct i2c_client *client, const struct i2c_device_id *id set_bit(EV_SYN, ts->input_dev->evbit); set_bit(EV_ABS, ts->input_dev->evbit); set_bit(EV_KEY, ts->input_dev->evbit); - set_bit(KEY_POWER, ts->input_dev->keybit); + set_bit(KEY_DOUBLE_TAP, ts->input_dev->keybit); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); #endif diff --git a/include/linux/input.h b/include/linux/input.h index 586c6e1de1fa..e2a0d30578b6 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -473,6 +473,7 @@ struct input_keymap_entry { #define KEY_SIMSWITCH 249 /* Multi SIM Switch key */ #define KEY_HOTKEY 250 /* Quick Clip key*/ +#define KEY_DOUBLE_TAP 251 /* Code 255 is reserved for special needs of AT keyboard driver */ From 6d6f280a3d645946e680d926c9eae264db14a0c8 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 18 Mar 2014 20:52:27 +0900 Subject: [PATCH 048/104] net: add a sysctl to reflect the fwmark on replies Kernel-originated IP packets that have no user socket associated with them (e.g., ICMP errors and echo replies, TCP RSTs, etc.) are emitted with a mark of zero. Add a sysctl to make them have the same mark as the packet they are replying to. This allows an administrator that wishes to do so to use mark-based routing, firewalling, etc. for these replies by marking the original packets inbound. Tested using user-mode linux: - ICMP/ICMPv6 echo replies and errors. - TCP RST packets (IPv4 and IPv6). Change-Id: I95d896647b278d092ef331d1377b959da1deb042 Signed-off-by: Lorenzo Colitti --- Documentation/networking/ip-sysctl.txt | 14 ++++++++++++++ include/net/ip.h | 3 +++ include/net/ipv6.h | 3 +++ include/net/netns/ipv4.h | 1 + include/net/netns/ipv6.h | 1 + net/ipv4/icmp.c | 11 +++++++++-- net/ipv4/ip_output.c | 3 ++- net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv6/icmp.c | 6 ++++++ net/ipv6/sysctl_net_ipv6.c | 7 +++++++ net/ipv6/tcp_ipv6.c | 1 + 11 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 2d4a6db25f0c..4af594604e9d 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -22,6 +22,13 @@ ip_no_pmtu_disc - BOOLEAN min_pmtu - INTEGER default 552 - minimum discovered Path MTU +fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv4 reply packets that are not + associated with a socket for example, TCP RSTs or ICMP echo replies). + If unset, these packets have a fwmark of zero. If set, they have the + fwmark of the packet they are replying to. + Default: 0 + route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. @@ -1048,6 +1055,13 @@ proxy_ndp - INTEGER 2 NDP packets are sent to userspace, where a userspace proxy can be implemented +fwmark_reflect - BOOLEAN + Controls the fwmark of kernel-generated IPv6 reply packets that are not + associated with a socket for example, TCP RSTs or ICMPv6 echo replies). + If unset, these packets have a fwmark of zero. If set, they have the + fwmark of the packet they are replying to. + Default: 0 + conf/interface/*: Change special settings per interface. diff --git a/include/net/ip.h b/include/net/ip.h index b53d65f24f7b..f6c3f0282388 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -237,6 +237,9 @@ extern void ipfrag_init(void); extern void ip_static_sysctl_init(void); +#define IP4_REPLY_MARK(net, mark) \ + ((net)->ipv4.sysctl_fwmark_reflect ? (mark) : 0) + static inline bool ip_is_fragment(const struct iphdr *iph) { return (iph->frag_off & htons(IP_MF | IP_OFFSET)) != 0; diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e4170a22fc6f..f55c78ec8520 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -109,6 +109,9 @@ struct frag_hdr { #define IP6_MF 0x0001 +#define IP6_REPLY_MARK(net, mark) \ + ((net)->ipv6.sysctl.fwmark_reflect ? (mark) : 0) + #include /* sysctls */ diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index bbd023a1c9b9..446db341c5b2 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -56,6 +56,7 @@ struct netns_ipv4 { unsigned int sysctl_ping_group_range[2]; long sysctl_tcp_mem[3]; + int sysctl_fwmark_reflect; atomic_t rt_genid; atomic_t dev_addr_genid; diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 81abfcb2eb4e..20b76abcb15e 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -25,6 +25,7 @@ struct netns_sysctl_ipv6 { int ip6_rt_mtu_expires; int ip6_rt_min_advmss; int icmpv6_time; + int fwmark_reflect; }; struct netns_ipv6 { diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 2cb2bf845641..eed77def6d96 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -335,6 +335,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) struct sock *sk; struct inet_sock *inet; __be32 daddr; + u32 mark = IP4_REPLY_MARK(net, skb->mark); if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) return; @@ -347,6 +348,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) icmp_param->data.icmph.checksum = 0; inet->tos = ip_hdr(skb)->tos; + sk->sk_mark = mark; daddr = ipc.addr = ip_hdr(skb)->saddr; ipc.opt = NULL; ipc.tx_flags = 0; @@ -358,6 +360,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) memset(&fl4, 0, sizeof(fl4)); fl4.daddr = daddr; fl4.saddr = rt->rt_spec_dst; + fl4.flowi4_mark = mark; fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos); fl4.flowi4_proto = IPPROTO_ICMP; security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); @@ -376,7 +379,7 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4, struct sk_buff *skb_in, const struct iphdr *iph, - __be32 saddr, u8 tos, + __be32 saddr, u8 tos, u32 mark, int type, int code, struct icmp_bxm *param) { @@ -388,6 +391,7 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->daddr = (param->replyopts.opt.opt.srr ? param->replyopts.opt.opt.faddr : iph->saddr); fl4->saddr = saddr; + fl4->flowi4_mark = mark; fl4->flowi4_tos = RT_TOS(tos); fl4->flowi4_proto = IPPROTO_ICMP; fl4->fl4_icmp_type = type; @@ -485,6 +489,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) struct flowi4 fl4; __be32 saddr; u8 tos; + u32 mark; struct net *net; struct sock *sk; @@ -581,6 +586,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) : iph->tos; + mark = IP4_REPLY_MARK(net, skb_in->mark); if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock; @@ -597,11 +603,12 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) icmp_param.skb = skb_in; icmp_param.offset = skb_network_offset(skb_in); inet_sk(sk)->tos = tos; + sk->sk_mark = mark; ipc.addr = iph->saddr; ipc.opt = &icmp_param.replyopts.opt; ipc.tx_flags = 0; - rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, + rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, mark, type, code, &icmp_param); if (IS_ERR(rt)) goto out_unlock; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 4910176d24ed..859ac52913a3 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1500,7 +1500,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, daddr = replyopts.opt.opt.faddr; } - flowi4_init_output(&fl4, arg->bound_dev_if, 0, + flowi4_init_output(&fl4, arg->bound_dev_if, + IP4_REPLY_MARK(sock_net(sk), skb->mark), RT_TOS(arg->tos), RT_SCOPE_UNIVERSE, sk->sk_protocol, ip_reply_arg_flowi_flags(arg), diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 7a7724da9bff..2de8aa32f3d7 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -765,6 +765,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = ipv4_tcp_mem, }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv4.sysctl_fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 27ac95a63429..fb8de37f32d3 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -382,6 +382,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) int len; int hlimit; int err = 0; + u32 mark = IP6_REPLY_MARK(net, skb->mark); if ((u8 *)hdr < skb->head || (skb->network_header + sizeof(*hdr)) > skb->tail) @@ -447,6 +448,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) fl6.daddr = hdr->saddr; if (saddr) fl6.saddr = *saddr; + fl6.flowi6_mark = mark; fl6.flowi6_oif = iif; fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; @@ -455,6 +457,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!icmpv6_xrlim_allow(sk, type, &fl6)) @@ -529,6 +532,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct dst_entry *dst; int err = 0; int hlimit; + u32 mark = IP6_REPLY_MARK(net, skb->mark); saddr = &ipv6_hdr(skb)->daddr; @@ -545,11 +549,13 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.saddr = *saddr; fl6.flowi6_oif = skb->dev->ifindex; fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; + fl6.flowi6_mark = mark; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); if (sk == NULL) return; + sk->sk_mark = mark; np = inet6_sk(sk); if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 166a57c47d39..1f872c332fc0 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -48,6 +48,13 @@ static ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "fwmark_reflect", + .data = &init_net.ipv6.sysctl.fwmark_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 98256cf72f9d..035812b2428b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -897,6 +897,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, fl6.flowi6_proto = IPPROTO_TCP; fl6.flowi6_oif = inet6_iif(skb); + fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark); fl6.fl6_dport = t1->dest; fl6.fl6_sport = t1->source; security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); From fd0a1225618e78eccd783ca1497cdb95cb80433b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 31 Mar 2014 16:23:51 +0900 Subject: [PATCH 049/104] net: core: Support UID-based routing. This contains the following commits: 1. 0149763 net: core: Add a UID range to fib rules. 2. 1650474 net: core: Use the socket UID in routing lookups. 3. 0b16771 net: ipv4: Add the UID to the route cache. 4. ee058f1 net: core: Add a RTA_UID attribute to routes. This is so that userspace can do per-UID route lookups. Bug: 15413527 Change-Id: I1285474c6734614d3bda6f61d88dfe89a4af7892 Signed-off-by: Lorenzo Colitti Conflicts: net/ipv6/ping.c --- include/linux/fib_rules.h | 2 + include/linux/rtnetlink.h | 2 + include/net/fib_rules.h | 6 +- include/net/flow.h | 8 +- include/net/ip.h | 1 + include/net/route.h | 6 +- net/core/fib_rules.c | 59 +++++++- net/ipv4/fib_frontend.c | 1 + net/ipv4/inet_connection_sock.c | 6 +- net/ipv4/ip_output.c | 3 +- net/ipv4/ping.c | 3 +- net/ipv4/raw.c | 3 +- net/ipv4/route.c | 12 ++ net/ipv4/syncookies.c | 3 +- net/ipv4/udp.c | 3 +- net/ipv4/xfrm4_policy.c | 1 + net/ipv6/af_inet6.c | 1 + net/ipv6/datagram.c | 1 + net/ipv6/inet6_connection_sock.c | 2 + net/ipv6/ping.c | 231 +++++++++++++++++++++++++++++++ net/ipv6/raw.c | 1 + net/ipv6/route.c | 6 + net/ipv6/syncookies.c | 1 + net/ipv6/tcp_ipv6.c | 3 + net/ipv6/udp.c | 1 + 25 files changed, 354 insertions(+), 12 deletions(-) create mode 100644 net/ipv6/ping.c diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h index 51da65b68b85..9dcdb6251cb8 100644 --- a/include/linux/fib_rules.h +++ b/include/linux/fib_rules.h @@ -49,6 +49,8 @@ enum { FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, + FRA_UID_START, /* UID range */ + FRA_UID_END, __FRA_MAX }; diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 577592ea0ea0..5529245a4a10 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -283,6 +283,8 @@ enum rtattr_type_t { RTA_MP_ALGO, /* no longer used */ RTA_TABLE, RTA_MARK, + RTA_MFC_STATS, /* not used - backported from the future */ + RTA_UID, __RTA_MAX }; diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 075f1e3a0fed..52e77a366bfc 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -23,6 +23,8 @@ struct fib_rule { struct fib_rule __rcu *ctarget; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; + uid_t uid_start; + uid_t uid_end; struct rcu_head rcu; struct net * fr_net; }; @@ -79,7 +81,9 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_GOTO] = { .type = NLA_U32 } + [FRA_GOTO] = { .type = NLA_U32 }, \ + [FRA_UID_START] = { .type = NLA_U32 }, \ + [FRA_UID_END] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) { diff --git a/include/net/flow.h b/include/net/flow.h index 6c469dbdb917..3fe9261baacf 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -23,6 +23,7 @@ struct flowi_common { #define FLOWI_FLAG_PRECOW_METRICS 0x02 #define FLOWI_FLAG_CAN_SLEEP 0x04 __u32 flowic_secid; + uid_t flowic_uid; }; union flowi_uli { @@ -59,6 +60,7 @@ struct flowi4 { #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid +#define flowi4_uid __fl_common.flowic_uid /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; @@ -78,7 +80,8 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be16 sport) + __be16 dport, __be16 sport, + uid_t uid) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; @@ -88,6 +91,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->flowi4_proto = proto; fl4->flowi4_flags = flags; fl4->flowi4_secid = 0; + fl4->flowi4_uid = uid; fl4->daddr = daddr; fl4->saddr = saddr; fl4->fl4_dport = dport; @@ -115,6 +119,7 @@ struct flowi6 { #define flowi6_proto __fl_common.flowic_proto #define flowi6_flags __fl_common.flowic_flags #define flowi6_secid __fl_common.flowic_secid +#define flowi6_uid __fl_common.flowic_uid struct in6_addr daddr; struct in6_addr saddr; __be32 flowlabel; @@ -158,6 +163,7 @@ struct flowi { #define flowi_proto u.__fl_common.flowic_proto #define flowi_flags u.__fl_common.flowic_flags #define flowi_secid u.__fl_common.flowic_secid +#define flowi_uid u.__fl_common.flowic_uid } __attribute__((__aligned__(BITS_PER_LONG/8))); static inline struct flowi *flowi4_to_flowi(struct flowi4 *fl4) diff --git a/include/net/ip.h b/include/net/ip.h index f6c3f0282388..e36ffa109b5f 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -166,6 +166,7 @@ struct ip_reply_arg { /* -1 if not needed */ int bound_dev_if; u8 tos; + uid_t uid; }; #define IP_REPLY_ARG_NOSRCCHECK 1 diff --git a/include/net/route.h b/include/net/route.h index b1c0d5b564c2..335466d46f50 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -60,6 +60,7 @@ struct rtable { int rt_iif; int rt_oif; __u32 rt_mark; + uid_t rt_uid; /* Info on neighbour */ __be32 rt_gateway; @@ -146,7 +147,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport); + daddr, saddr, dport, sport, sock_i_uid(sk)); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -250,7 +251,8 @@ static inline void ip_route_connect_init(struct flowi4 *fl4, __be32 dst, __be32 flow_flags |= FLOWI_FLAG_CAN_SLEEP; flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, - protocol, flow_flags, dst, src, dport, sport); + protocol, flow_flags, dst, src, dport, sport, + sock_i_uid(sk)); } static inline struct rtable *ip_route_connect(struct flowi4 *fl4, diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index c02e63c908da..91ede8b74f44 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -17,6 +17,12 @@ #include #include +#define INVALID_UID ((uid_t) -1) +#define uid_valid(uid) ((uid) != -1) +#define uid_lte(a, b) ((a) <= (b)) +#define uid_eq(a, b) ((a) == (b)) +#define uid_gte(a, b) ((a) >= (b)) + int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { @@ -31,6 +37,8 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->pref = pref; r->table = table; r->flags = flags; + r->uid_start = INVALID_UID; + r->uid_end = INVALID_UID; r->fr_net = hold_net(ops->fro_net); /* The lock is not required here, the list in unreacheable @@ -177,6 +185,23 @@ void fib_rules_unregister(struct fib_rules_ops *ops) } EXPORT_SYMBOL_GPL(fib_rules_unregister); +static inline uid_t fib_nl_uid(struct nlattr *nla) +{ + return nla_get_u32(nla); +} + +static int nla_put_uid(struct sk_buff *skb, int idx, uid_t uid) +{ + return nla_put_u32(skb, idx, uid); +} + +static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule) +{ + return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) || + (uid_gte(fl->flowi_uid, rule->uid_start) && + uid_lte(fl->flowi_uid, rule->uid_end)); +} + static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, struct flowi *fl, int flags) { @@ -191,6 +216,9 @@ static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops, if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask) goto out; + if (!fib_uid_range_match(fl, rule)) + goto out; + ret = ops->match(rule, fl, flags); out: return (rule->flags & FIB_RULE_INVERT) ? !ret : ret; @@ -361,6 +389,19 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) } else if (rule->action == FR_ACT_GOTO) goto errout_free; + /* UID start and end must either both be valid or both unspecified. */ + rule->uid_start = rule->uid_end = INVALID_UID; + if (tb[FRA_UID_START] || tb[FRA_UID_END]) { + if (tb[FRA_UID_START] && tb[FRA_UID_END]) { + rule->uid_start = fib_nl_uid(tb[FRA_UID_START]); + rule->uid_end = fib_nl_uid(tb[FRA_UID_END]); + } + if (!uid_valid(rule->uid_start) || + !uid_valid(rule->uid_end) || + !uid_lte(rule->uid_start, rule->uid_end)) + goto errout_free; + } + err = ops->configure(rule, skb, frh, tb); if (err < 0) goto errout_free; @@ -466,6 +507,14 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg) (rule->mark_mask != nla_get_u32(tb[FRA_FWMASK]))) continue; + if (tb[FRA_UID_START] && + !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START]))) + continue; + + if (tb[FRA_UID_END] && + !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END]))) + continue; + if (!ops->compare(rule, frh, tb)) continue; @@ -520,7 +569,9 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + nla_total_size(4) /* FRA_FWMARK */ - + nla_total_size(4); /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_FWMASK */ + + nla_total_size(4) /* FRA_UID_START */ + + nla_total_size(4); /* FRA_UID_END */ if (ops->nlmsg_payload) payload += ops->nlmsg_payload(rule); @@ -578,6 +629,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, if (rule->target) NLA_PUT_U32(skb, FRA_GOTO, rule->target); + if (uid_valid(rule->uid_start)) + nla_put_uid(skb, FRA_UID_START, rule->uid_start); + + if (uid_valid(rule->uid_end)) + nla_put_uid(skb, FRA_UID_END, rule->uid_end); + if (ops->fill(rule, skb, frh) < 0) goto nla_put_failure; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index cbe3a68507cf..0a24199ff848 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -481,6 +481,7 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { [RTA_METRICS] = { .type = NLA_NESTED }, [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 19d66cefd7d3..b902d589b126 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -365,7 +365,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -398,7 +399,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 859ac52913a3..6f72f395eb13 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1506,7 +1506,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, __be32 daddr, RT_SCOPE_UNIVERSE, sk->sk_protocol, ip_reply_arg_flowi_flags(arg), daddr, rt->rt_spec_dst, - tcp_hdr(skb)->source, tcp_hdr(skb)->dest); + tcp_hdr(skb)->source, tcp_hdr(skb)->dest, + arg->uid); security_skb_classify_flow(skb, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 50009c787bcd..7859ce552dfc 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -560,7 +560,8 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, - inet_sk_flowi_flags(sk), faddr, saddr, 0, 0); + inet_sk_flowi_flags(sk), faddr, saddr, 0, 0, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(&fl4)); rt = ip_route_output_flow(net, &fl4, sk); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index bbd604c68e68..b5b563d10a37 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -567,7 +567,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP, - daddr, saddr, 0, 0); + daddr, saddr, 0, 0, + sock_i_uid(sk)); if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl4, msg); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d02a8da2365b..3ab9d27f5ab3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -741,6 +741,7 @@ static inline int compare_keys(struct rtable *rt1, struct rtable *rt2) (rt1->rt_mark ^ rt2->rt_mark) | (rt1->rt_key_tos ^ rt2->rt_key_tos) | (rt1->rt_route_iif ^ rt2->rt_route_iif) | + (rt1->rt_uid ^ rt2->rt_uid) | (rt1->rt_oif ^ rt2->rt_oif)) == 0; } @@ -1881,6 +1882,7 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt) fl4.flowi4_oif = rt->dst.dev->ifindex; fl4.flowi4_iif = skb->dev->ifindex; fl4.flowi4_mark = skb->mark; + fl4.flowi4_uid = skb->sk ? sock_i_uid(skb->sk) : 0; rcu_read_lock(); if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0) @@ -2064,6 +2066,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2193,6 +2196,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->rt_iif = in_dev->dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2376,6 +2380,7 @@ out: return err; rth->rt_iif = dev->ifindex; rth->rt_oif = 0; rth->rt_mark = skb->mark; + rth->rt_uid = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_peer_genid = 0; @@ -2580,6 +2585,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, rth->rt_iif = orig_oif ? : dev_out->ifindex; rth->rt_oif = orig_oif; rth->rt_mark = fl4->flowi4_mark; + rth->rt_uid = fl4->flowi4_uid; rth->rt_gateway = fl4->daddr; rth->rt_spec_dst= fl4->saddr; rth->rt_peer_genid = 0; @@ -2831,6 +2837,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp4) rt_is_output_route(rth) && rth->rt_oif == flp4->flowi4_oif && rth->rt_mark == flp4->flowi4_mark && + rth->rt_uid == flp4->flowi4_uid && !((rth->rt_key_tos ^ flp4->flowi4_tos) & (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && @@ -2912,6 +2919,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_iif = ort->rt_iif; rt->rt_oif = ort->rt_oif; rt->rt_mark = ort->rt_mark; + rt->rt_uid = ort->rt_uid; rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; @@ -3007,6 +3015,9 @@ static int rt_fill_info(struct net *net, if (rt->rt_mark) NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); + if (rt->rt_uid != (uid_t) -1) + NLA_PUT_BE32(skb, RTA_UID, rt->rt_uid); + error = rt->dst.error; if (peer) { inet_peer_refcheck(rt->peer); @@ -3126,6 +3137,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void .flowi4_tos = rtm->rtm_tos, .flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0, .flowi4_mark = mark, + .flowi4_uid = tb[RTA_UID] ? nla_get_u32(tb[RTA_UID]) : current_uid(), }; rt = ip_route_output_key(net, &fl4); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index eab2a7fb15d1..7f4dba3fce6c 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -351,7 +351,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, - ireq->loc_addr, th->source, th->dest); + ireq->loc_addr, th->source, th->dest, + sock_i_uid(sk)); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index fe141052a1be..4d844344f262 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -931,7 +931,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP, - faddr, saddr, dport, inet->inet_sport); + faddr, saddr, dport, inet->inet_sport, + sock_i_uid(sk)); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index a0b4c5da8d43..e8ee4279fd22 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -86,6 +86,7 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, xdst->u.rt.rt_iif = fl4->flowi4_iif; xdst->u.rt.rt_oif = fl4->flowi4_oif; xdst->u.rt.rt_mark = fl4->flowi4_mark; + xdst->u.rt.rt_uid = fl4->flowi4_uid; xdst->u.dst.dev = dev; dev_hold(dev); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 29625e9a51a6..1f7ab56f4215 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -701,6 +701,7 @@ int inet6_sk_rebuild_header(struct sock *sk) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 76832c8dc89d..ae4d713ac88d 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -160,6 +160,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST)) fl6.flowi6_oif = np->mcast_oif; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index 02dd203d9eac..21ee002710f2 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -72,6 +72,7 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); @@ -223,6 +224,7 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_sport = inet->inet_sport; fl6.fl6_dport = inet->inet_dport; + fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); final_p = fl6_update_dst(&fl6, np->opt, &final); diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c new file mode 100644 index 000000000000..0db481c6e16a --- /dev/null +++ b/net/ipv6/ping.c @@ -0,0 +1,231 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * "Ping" sockets + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Based on ipv4/ping.c code. + * + * Authors: Lorenzo Colitti (IPv6 support) + * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6), + * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct proto pingv6_prot = { + .name = "PINGv6", + .owner = THIS_MODULE, + .init = ping_init_sock, + .close = ping_close, + .connect = ip6_datagram_connect, + .disconnect = udp_disconnect, + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .sendmsg = ping_v6_sendmsg, + .recvmsg = ping_recvmsg, + .bind = ping_bind, + .backlog_rcv = ping_queue_rcv_skb, + .hash = ping_hash, + .unhash = ping_unhash, + .get_port = ping_get_port, + .obj_size = sizeof(struct raw6_sock), +}; +EXPORT_SYMBOL_GPL(pingv6_prot); + +static struct inet_protosw pingv6_protosw = { + .type = SOCK_DGRAM, + .protocol = IPPROTO_ICMPV6, + .prot = &pingv6_prot, + .ops = &inet6_dgram_ops, + .no_check = UDP_CSUM_DEFAULT, + .flags = INET_PROTOSW_REUSE, +}; + + +/* Compatibility glue so we can support IPv6 when it's compiled as a module */ +int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) +{ + return -EAFNOSUPPORT; +} +int dummy_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) +{ + return -EAFNOSUPPORT; +} +int dummy_icmpv6_err_convert(u8 type, u8 code, int *err) +{ + return -EAFNOSUPPORT; +} +void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload) {} +int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr, + struct net_device *dev, int strict) +{ + return 0; +} + +int __init pingv6_init(void) +{ + pingv6_ops.ipv6_recv_error = ipv6_recv_error; + pingv6_ops.datagram_recv_ctl = datagram_recv_ctl; + pingv6_ops.icmpv6_err_convert = icmpv6_err_convert; + pingv6_ops.ipv6_icmp_error = ipv6_icmp_error; + pingv6_ops.ipv6_chk_addr = ipv6_chk_addr; + return inet6_register_protosw(&pingv6_protosw); +} + +/* This never gets called because it's not possible to unload the ipv6 module, + * but just in case. + */ +void pingv6_exit(void) +{ + pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error; + pingv6_ops.datagram_recv_ctl = dummy_datagram_recv_ctl; + pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert; + pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error; + pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr; + inet6_unregister_protosw(&pingv6_protosw); +} + +int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len) +{ + struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); + struct icmp6hdr user_icmph; + int addr_type; + struct in6_addr *daddr; + int iif = 0; + struct flowi6 fl6; + int err; + int hlimit; + struct dst_entry *dst; + struct rt6_info *rt; + struct pingfakehdr pfh; + + pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); + + err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, + sizeof(user_icmph)); + if (err) + return err; + + if (msg->msg_name) { + struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name; + if (msg->msg_namelen < sizeof(struct sockaddr_in6) || + u->sin6_family != AF_INET6) { + return -EINVAL; + } + if (sk->sk_bound_dev_if && + sk->sk_bound_dev_if != u->sin6_scope_id) { + return -EINVAL; + } + daddr = &(u->sin6_addr); + iif = u->sin6_scope_id; + } else { + if (sk->sk_state != TCP_ESTABLISHED) + return -EDESTADDRREQ; + daddr = &np->daddr; + } + + if (!iif) + iif = sk->sk_bound_dev_if; + + addr_type = ipv6_addr_type(daddr); + if (__ipv6_addr_needs_scope_id(addr_type) && !iif) + return -EINVAL; + if (addr_type & IPV6_ADDR_MAPPED) + return -EINVAL; + + /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ + + memset(&fl6, 0, sizeof(fl6)); + + fl6.flowi6_proto = IPPROTO_ICMPV6; + fl6.saddr = np->saddr; + fl6.daddr = *daddr; + fl6.fl6_icmp_type = user_icmph.icmp6_type; + fl6.fl6_icmp_code = user_icmph.icmp6_code; + fl6.flowi6_uid = sock_i_uid(sk); + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + + if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) + fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; + + dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1); + if (IS_ERR(dst)) + return PTR_ERR(dst); + rt = (struct rt6_info *) dst; + + np = inet6_sk(sk); + if (!np) + return -EBADF; + + if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) + fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; + + pfh.icmph.type = user_icmph.icmp6_type; + pfh.icmph.code = user_icmph.icmp6_code; + pfh.icmph.checksum = 0; + pfh.icmph.un.echo.id = inet->inet_sport; + pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; + pfh.iov = msg->msg_iov; + pfh.wcheck = 0; + pfh.family = AF_INET6; + + if (ipv6_addr_is_multicast(&fl6.daddr)) + hlimit = np->mcast_hops; + else + hlimit = np->hop_limit; + if (hlimit < 0) + hlimit = ip6_dst_hoplimit(dst); + +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [START] */ + lock_sock(sk); +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [END] */ + + err = ip6_append_data(sk, ping_getfrag, &pfh, len, + 0, hlimit, + np->tclass, NULL, &fl6, rt, + MSG_DONTWAIT, np->dontfrag); + + if (err) { + ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev, + ICMP6_MIB_OUTERRORS); + ip6_flush_pending_frames(sk); + } else { + err = icmpv6_push_pending_frames(sk, &fl6, + (struct icmp6hdr *) &pfh.icmph, + len); + } + +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [START] */ + release_sock(sk); + if(err) +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [END] */ + return err; + +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [START] */ + return len; +/* 2013-11-25 hobbes.song LGP_DATA_CTS_IPV6_PINGTEST [END] */ + +} diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 5bddea778840..49ec3f8e7cea 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -761,6 +761,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (sin6) { if (addr_len < SIN6_LEN_RFC2133) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index bc4888d902b2..b503feda4188 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2278,6 +2278,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_IIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_UID] = { .type = NLA_U32 }, }; static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, @@ -2590,6 +2591,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void if (tb[RTA_OIF]) oif = nla_get_u32(tb[RTA_OIF]); + if (tb[RTA_UID]) + fl6.flowi6_uid = nla_get_u32(tb[RTA_UID]); + else + fl6.flowi6_uid = (iif ? (uid_t) -1 : current_uid()); + if (iif) { struct net_device *dev; int flags = 0; diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 8e951d8d3b81..83ee321c08c7 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -244,6 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 035812b2428b..059eb63fca1f 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -251,6 +251,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = usin->sin6_port; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); final_p = fl6_update_dst(&fl6, np->opt, &final); @@ -404,6 +405,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet->inet_dport; fl6.fl6_sport = inet->inet_sport; + fl6.flowi6_uid = sock_i_uid(sk); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, NULL, false); @@ -496,6 +498,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, fl6.flowi6_mark = sk->sk_mark; fl6.fl6_dport = inet_rsk(req)->rmt_port; fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.flowi6_uid = sock_i_uid(sk); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); opt = np->opt; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 37b0699e95e5..a20d55dc9c2a 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1087,6 +1087,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_mark = sk->sk_mark; + fl6.flowi6_uid = sock_i_uid(sk); if (msg->msg_controllen) { opt = &opt_space; From bee0a2f0b28b301b2c46e435c0174098b8a86f15 Mon Sep 17 00:00:00 2001 From: Sreeram Ramachandran Date: Tue, 8 Jul 2014 11:37:03 -0700 Subject: [PATCH 050/104] Handle 'sk' being NULL in UID-based routing. Bug: 15413527 Change-Id: If33bebb7b52c0ebfa8dac2452607bce0c2b0faa0 Signed-off-by: Sreeram Ramachandran --- include/net/route.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/route.h b/include/net/route.h index 335466d46f50..7488c9ed1033 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -147,7 +147,7 @@ static inline struct rtable *ip_route_output_ports(struct net *net, struct flowi flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos, RT_SCOPE_UNIVERSE, proto, sk ? inet_sk_flowi_flags(sk) : 0, - daddr, saddr, dport, sport, sock_i_uid(sk)); + daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : 0); if (sk) security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); From 7a8cc62a4059511cf9dbe2f336399fa294ed48ed Mon Sep 17 00:00:00 2001 From: vm03 Date: Mon, 23 Mar 2015 23:05:45 +0300 Subject: [PATCH 051/104] add w5n dts and config --- .../msm8610-w5n_global_com/msm8610-v1-w5n.dts | 39 ++ .../msm8610-w5n_global_com/msm8610-v2-w5n.dts | 39 ++ .../msm8610-w5n-camera.dtsi | 409 ++++++++++++++ .../msm8610-w5n-hdmi.dtsi | 15 + .../msm8610-w5n-input.dtsi | 94 ++++ .../msm8610-w5n-misc.dtsi | 54 ++ .../msm8610-w5n-nfc.dtsi | 51 ++ .../msm8610-w5n-panel.dtsi | 45 ++ .../msm8610-w5n-pm.dtsi | 299 ++++++++++ .../msm8610-w5n-sensor.dtsi | 139 +++++ .../msm8610-w5n-usb.dtsi | 15 + .../msm8610-w5n_global_com/msm8610-w5n.dtsi | 156 ++++++ arch/arm/configs/w5n_global_com_defconfig | 522 ++++++++++++++++++ 13 files changed, 1877 insertions(+) create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v1-w5n.dts create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v2-w5n.dts create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-hdmi.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-misc.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-nfc.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-panel.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-pm.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-sensor.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-usb.dtsi create mode 100644 arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n.dtsi create mode 100644 arch/arm/configs/w5n_global_com_defconfig diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v1-w5n.dts b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v1-w5n.dts new file mode 100644 index 000000000000..631922b7b25f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v1-w5n.dts @@ -0,0 +1,39 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/include/ "../msm8610-lge-common/msm8610-v1-lge.dtsi" +/include/ "msm8610-w5n.dtsi" + + +/ { + model = "Qualcomm MSM 8610v1 w5nglobalcom"; + compatible = "qcom,msm8610-w5nglobalcom", "qcom,msm8610"; + /* + MSM8610 = 147, + MSM8110 = 161, + MSM8210 = 162, + MSM8810 = 163, + MSM8212 = 164, + MSM8612 = 165, + MSM8812 = 166, + */ + qcom,board-id = <122 0>; + aliases { + lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video"; + lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video"; + }; +}; + + + + diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v2-w5n.dts b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v2-w5n.dts new file mode 100644 index 000000000000..8ffe2cb04b50 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-v2-w5n.dts @@ -0,0 +1,39 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +/include/ "../msm8610-lge-common/msm8610-v2-lge.dtsi" +/include/ "msm8610-w5n.dtsi" + + +/ { + model = "Qualcomm MSM 8610v2 w5nglobalcom"; + compatible = "qcom,msm8610-w5nglobalcom", "qcom,msm8610"; + /* + MSM8610 = 147, + MSM8110 = 161, + MSM8210 = 162, + MSM8810 = 163, + MSM8212 = 164, + MSM8612 = 165, + MSM8812 = 166, + */ + qcom,board-id = <122 0>; + aliases { + lcd_primary = "/soc/qcom,dsi_v2_lgd_incell_wvga_video"; + lcd_secondary = "/soc/qcom,dsi_v2_tovis_shrink_wvga_video"; + }; +}; + + + + diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi new file mode 100644 index 000000000000..0986ea0c39d9 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + i2c@0 { + led_flash0: qcom,led-flash@39 { + compatible = "qcom,led-flash"; + status = "ok"; + reg = <0x39>; + rt8542,lcd_bl_en = <&msmgpio 60 0x00>; + rt8542,max_current = <0x0C>; + rt8542,min_brightness = <0x04>; + rt8542,default_brightness = <0x66>; + rt8542,max_brightness = <0x7D>; + rt8542,enable_pwm = <0>; + rt8542,blmap_size = <127>; + rt8542,blmap = < + 5 5 5 5 5 5 5 5 5 5 //9 + 5 5 5 6 6 6 6 6 6 6 //19 + 6 6 6 6 6 6 7 7 7 7 //29 + 7 7 7 8 8 8 8 9 9 9 //39 + 10 10 10 11 11 11 12 12 12 12 //49 + 13 13 14 14 14 14 15 15 15 16 //59 + 17 18 19 20 20 21 22 23 23 24 //69 + 24 25 25 25 26 26 26 27 27 27 //79 + 27 28 29 30 31 32 33 34 35 36 //89 + 37 38 38 39 40 41 42 43 44 45 //99 + 46 47 48 49 50 52 53 54 55 56 //109 + 57 58 59 60 61 63 64 65 67 68 //119 + 70 71 73 75 77 79 80>; + + cell-index = <0>; + qcom,flash-name = "rt8542"; + qcom,slave-id = <0x39 0x00 0x0011>; + qcom,flash-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 18 0>; + qcom,gpio-flash-en = <0>; + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "FLASH_EN"; + + }; + }; +}; + +&i2c { + +/* IMX219(8M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator2: qcom,actuator_2@18 { + cell-index = <4>; //Use Af_main_0 of dw9714 with IMX219(8M) + reg = <0x18 0x0>; + compatible = "qcom,actuator"; + }; +/* IMX111(8M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator1: qcom,actuator_1@18 { + cell-index = <1>; //Use Af_main_0 of dw9714 with IMX111(8M) + reg = <0x19 0x0>; + compatible = "qcom,actuator"; +}; +/* HI543(5M) actuator */ +/* because of alternative sensor module usages, Actuator address are 0x18s, + but somehow without CCI & Platform DD, It is impossible to share the same No. of address with other DT actuator nodes. + So, as a workaround, we add one by one on the address as no. of array goes on. 0x18, 0x19 and 0x1A which used to be 0x18, 0x18 and 0x18 + This temporal address will be parsing in the msm_actuator_i2c_probe() of msm_actuator.c file. + youngwook.song@lge.com, 2014-02-20*/ + + actuator0: qcom,actuator_0@18 { + cell-index = <0>; //Use Af_main_0 of dw9716 with HI543(5M) + reg = <0x1A 0x0>; + compatible = "qcom,actuator"; + }; + +/* IMX111(8M) eeprom */ + eeprom1: msm_eeprom_imx111@50 { //EEPROM READ + cell-index = <0>; + reg = <0x50 0x0>; + qcom,eeprom-name = "imx111_eeprom"; + compatible = "msm_eeprom"; + qcom,slave-addr = <0x50>; + + qcom,num-blocks = <4>; + qcom,page0 = <0 0x0 1 0x0 1 20>; // valid size, addr, addr_t, data, data_t, delay + qcom,poll0 = <0 0x0 1 0x0 1 20>; + qcom,mem0 = <0x100 0x00 1 0 1 0>; //EEPROM READ + + qcom,page1 = <0 0x0 1 0x0 1 20>; + qcom,poll1 = <0 0x0 1 0x0 1 20>; + qcom,mem1 = <0x100 0x00 1 0 1 0>; + + qcom,page2 = <0 0x0 1 0x0 1 20>; + qcom,poll2 = <0 0x0 1 0x0 1 20>; + qcom,mem2 = <0x100 0x00 1 0 1 0>; + + qcom,page3 = <0 0x0 1 0x0 1 20>; + qcom,poll3 = <0 0x0 1 0x0 1 20>; + qcom,mem3 = <0xC6 0x00 1 0 1 0>; + + cam_vio-supply = <&pm8110_l7>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-type = <0>; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 20 0>; //2.8V VCM + qcom,gpio-standby = <0>; //CAM_STANDBY + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_STANDBY"; + + qcom,cam-power-seq-type = "sensor_vreg","sensor_gpio"; + qcom,cam-power-seq-val = "cam_vio","sensor_gpio_standby"; + qcom,cam-power-seq-cfg-val = <1 1>; + qcom,cam-power-seq-delay = <1 1>; + }; +/* HI543(5M) eeprom */ + eeprom0: msm_eeprom_hi543@14 { //EEPROM READ + cell-index = <0>; + reg = <0x14 0x0>; + qcom,eeprom-name = "hi543_eeprom"; + compatible = "msm_eeprom"; + qcom,slave-addr = <0x14>; + + qcom,num-blocks = <1>; + qcom,page0 = <0 0x0 1 0x0 1 20>; // valid size, addr, addr_t, data, data_t, delay + qcom,poll0 = <0 0x0 1 0x0 1 20>; + qcom,mem0 = <0x06 0x0000 2 0 1 0>; //EEPROM READ + + cam_vio-supply = <&pm8110_l7>; + qcom,cam-vreg-name = "cam_vio"; + qcom,cam-vreg-type = <0>; + qcom,cam-vreg-min-voltage = <1800000>; + qcom,cam-vreg-max-voltage = <1800000>; + qcom,cam-vreg-op-mode = <80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 20 0>; //2.8V VCM + qcom,gpio-standby = <0>; //CAM_STANDBY + qcom,gpio-req-tbl-num = <0>; + qcom,gpio-req-tbl-flags = <0>; + qcom,gpio-req-tbl-label = "CAM_STANDBY"; + + qcom,cam-power-seq-type = "sensor_vreg","sensor_gpio"; + qcom,cam-power-seq-val = "cam_vio","sensor_gpio_standby"; + qcom,cam-power-seq-cfg-val = <1 1>; + qcom,cam-power-seq-delay = <1 1>; + }; + +/* Rev a */ + hi543a: qcom,camera_rev_a@40 { + compatible = "qcom,hi543"; + reg = <0x40 0x0>; + qcom,slave-id = <0x40 0x0 0x1F3C>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator0>; + qcom,mount-angle = <90>; + qcom,sensor-name = "hi543"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 8000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 91 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_a"; + }; + + hi707a: qcom,camera_rev_a@60 { + compatible = "qcom,hi707"; + reg = <0x60>; + qcom,slave-id = <0x60 0x4 0xB8>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <270>; + qcom,sensor-name = "hi707"; + + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 8000 80000>; + qcom,sensor-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 14 0>, + <&msmgpio 15 0>, //VT_CAM_RESET_N, GPIO 15 + <&msmgpio 67 0>, //VT_CAM_PWDN, GPIO=67 + <&msmgpio 91 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", "CAM_STANDBY2", "MAIN_VANA_EN2"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x3>; + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + status = "okay"; + revision = "rev_a"; + }; +/* Rev B ~ */ + hi543: qcom,camera_rev_b@40 { + compatible = "qcom,hi543"; + reg = <0x40 0x0>; + qcom,slave-id = <0x40 0x0 0x1F3C>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator0>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom0>; + qcom,mount-angle = <90>; + qcom,sensor-name = "hi543"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "decide_in_lk"; + revision = "rev_b..."; + }; + + hi707: qcom,camera_rev_b@60 { + compatible = "qcom,hi707"; + reg = <0x60>; + qcom,slave-id = <0x60 0x4 0xB8>; + qcom,csiphy-sd-index = <1>; + qcom,csid-sd-index = <1>; + qcom,mount-angle = <270>; + qcom,sensor-name = "hi707"; + qcom,maker-gpio = <86>; + + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + qcom,sensor-type = <1>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 14 0>, + <&msmgpio 15 0>, //VT_CAM_RESET_N, GPIO 15 + <&msmgpio 67 0>, //VT_CAM_PWDN, GPIO=67 + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK2", "CAM_RESET2", "CAM_STANDBY2", "MAIN_VANA_EN2"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x3>; + qcom,sensor-position = <1>; + qcom,sensor-mode = <1>; + status = "okay"; + revision = "rev_b..."; + }; +/* imx111 Rev B ~ */ + imx111: qcom,camera_rev_b@20 { + compatible = "qcom,imx111"; + reg = <0x20 0x0>; + qcom,slave-id = <0x20 0x0 0x111>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator1>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom1>; + qcom,mount-angle = <90>; + qcom,sensor-name = "imx111"; + cam_vdig-supply = <&pm8110_l7>; /* Dummy VDIG Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio", "cam_vana"; + qcom,cam-vreg-type = <0 0 0>; + qcom,cam-vreg-min-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1300000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, + <&msmgpio 21 0>, + <&msmgpio 20 0>, + <&msmgpio 85 0>; + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; + qcom,gpio-vana = <3>; + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x7>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_b..."; + }; + + imx219: qcom,camera_rev_b@34 { + compatible = "qcom,imx219"; + reg = <0x34 0x0>; + qcom,slave-id = <0x34 0x0 0x219>; + qcom,csiphy-sd-index = <0>; + qcom,csid-sd-index = <0>; + qcom,actuator-src = <&actuator2>; + qcom,led-flash-src = <&led_flash0>; + qcom,eeprom-src = <&eeprom1>; + qcom,mount-angle = <90>; + qcom,sensor-name = "imx219"; + cam_vdig-supply = <&pm8110_l7>; //CAM_DVDD_1V8_1V2 + cam_vio-supply = <&pm8110_l7>; + cam_vana-supply = <&pm8110_l7>; /* Dummy VANA Setting, We never use this LDO7 for DIG, youngwook.song@lge.com, 2013.08.26 */ + qcom,cam-vreg-name = "cam_vdig", "cam_vio";//, "cam_vana"; + qcom,cam-vreg-type = <0 0>; + qcom,cam-vreg-min-voltage = <1200000 1800000 2850000>; + qcom,cam-vreg-max-voltage = <1200000 1800000 2850000>; + qcom,cam-vreg-op-mode = <200000 80000 80000>; + qcom,gpio-no-mux = <0>; + gpios = <&msmgpio 13 0>, //MAIN_CAM_MCLK + <&msmgpio 21 0>, //MAIN_RESET_N + <&msmgpio 20 0>, //VCM_PWDN, //CAM_VCM_2V8, MAIN_CAM0_VCM_PWDN=GPIO36 + <&msmgpio 85 0>; //CAM_AVDD_2V8, LDO1_EN=GPIO 62 + qcom,gpio-reset = <1>; + qcom,gpio-standby = <2>; // +VCM_2V8 (AF) + qcom,gpio-vana = <3>; // =CAM_AVDD_2V8 + qcom,gpio-req-tbl-num = <0 1 2 3>; + qcom,gpio-req-tbl-flags = <1 0 0 0>; + qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET1", "CAM_STANDBY", "MAIN_VANA_EN"; + qcom,csi-lane-assign = <0xe4>; + qcom,csi-lane-mask = <0x07>; + qcom,sensor-position = <0>; + qcom,sensor-mode = <0>; + status = "okay"; + revision = "rev_b..."; + }; +}; + +/ { + aliases { + hi543 = &hi543; /* 5M Main Camera */ + imx111 = &imx111; /* 8M Main Camera */ + imx219 = &imx219; /* 8M Main Camera */ + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-hdmi.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-hdmi.dtsi new file mode 100644 index 000000000000..030f0d89cc42 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-hdmi.dtsi @@ -0,0 +1,15 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/ { + +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi new file mode 100644 index 000000000000..a4ea90436d4f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi @@ -0,0 +1,94 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + i2c@f9923000{ + mms100s@48 { + melfas,product = "I2S45B"; + melfas,max-x = <480>; + melfas,max-y = <800>; + melfas,use_vdd_i2c = <1>; + melfas,gpio-vdd-en = <62>; + melfas,key-map = <158 139>; + melfas,fw-image-name = "melfas/w5n_global_com/w5n_global_com_rev_b_v101.mfsb"; + status = "ok"; + revision = "rev_b"; + }; + + mms100s@48_rev_c { + melfas,product = "I2S45B"; + melfas,max-x = <480>; + melfas,max-y = <800>; + melfas,use_vdd_i2c = <1>; + melfas,gpio-vdd-en = <82>; + melfas,key-map = <158 139>; + melfas,fw-image-name = "melfas/w5n_global_com/w5n_global_com_rev_b_v101.mfsb"; + status = "ok"; + revision = "rev_c"; + }; + + lgd_melfas@34 { + status = "ok"; + revision = "rev_a"; + }; + + ads_ags04@6a { + status = "ok"; + revision = "rev_a"; + }; + + synaptics_red@20 { + status = "disable"; + revision = "rev_d..."; + synaptics,button-map = <158 139>; + synaptics,gpio_vdd_en = <82>; + }; + + synaptics_s220x@20 { + status = "ok"; + revision = "rev_d..."; + synaptics,fw_version_info = <0x82 0x04 0x51>; + synaptics,fw_image = "synaptics/w5n_global_com/PLG389-V1.10-PR1600582-DS4.3.5.1.16_1204518A.img", /* panel id 0 */ + "synaptics/w5n_global_com/PLG317-V1.14-PR1600582-DS4.3.5.1.16_3204518E.img"; /* panel id 1 */ + synaptics,panel_spec = "synaptics/w5n_global_com/w5n_Suntel_limit.txt", /* panel id 0 */ + "synaptics/w5n_global_com/w5n_Inotek_limit.txt";/* panel id 1 */ + synaptics,global_access_pixel = <10>; + lge,knock_on_type = <1>; + synaptics,platform_data { + number_of_button = <2>; + button_name = [9e 8b]; + x_max = <960>; + y_max = <1600>; + lcd_x = <480>; + lcd_y = <800>; + use_vio_regulator = <1>; + gpio_vdd_en = <82>; + /*maker_id disable(0), enable(1)*/ + maker_id = <1>; + maker_id_gpio = <77>; + palm_detect_mode = <1>; + ghost_detection_enable = <0>; + ghost_detection_value = <200 5 4 6 5 5 10 500 100 6 5>; + }; + }; + }; + gpio_keys { + quick_clip { + label = "quick_clip"; + gpios = <&msmgpio 75 0x1>; + linux,input-type = <1>; + linux,code = <250>; + gpio-key,wakeup; + debounce-interval = <15>; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-misc.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-misc.dtsi new file mode 100644 index 000000000000..72634a2d6351 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-misc.dtsi @@ -0,0 +1,54 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + earjack-debugger { + status = "disable"; + }; + + i2c@0 { + rt8542@39 { + status = "ok"; + rt8542,max_current = <0x0C>; /* 12.1mA */ + rt8542,max_brightness = <105>; /* 105 */ + rt8542,blmap = < + 10 10 12 12 12 14 14 16 16 18 + 18 19 19 20 20 22 22 22 24 24 + 26 26 28 28 29 29 30 30 32 32 + 32 34 34 36 36 38 38 39 39 40 + 40 42 42 42 44 44 46 46 48 48 + 49 49 50 50 52 52 52 54 54 56 + 56 58 58 59 59 60 60 62 62 62 + 64 64 66 66 68 68 69 69 70 70 + 72 72 72 74 74 76 76 78 78 79 + 79 80 80 82 82 82 84 84 86 86 + 88 88 89 89 90 90 92 92 92 94 + 94 96 96 96 98 98 99 99 100 100 + 102 102 104 104 105 105 105>; + }; + lp5521@32{ + status = "no"; + revision = "rev_a..."; + ti,led_en = <&msmgpio 6 0x0>; + ti,i2c-pull-up = <1>; + ti,vddio_i2c-supply = <&pm8110_l14>; + ti,vddio_i2c_supply_min = <1800000>; + ti,vddio_i2c_supply_max = <1800000>; + ti,vddio_i2c_load_ua = <10000>; + + }; + }; + + hall-bu52061nvx { + status = "ok"; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-nfc.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-nfc.dtsi new file mode 100644 index 000000000000..5af622b8f6e7 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-nfc.dtsi @@ -0,0 +1,51 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* LGE_CHANGE, [NFC][garam.kim@lge.com], NFC Bring up temp */ +&soc { + + i2c@0 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 4 0x00>; + nxp,gpio_scl = <&msmgpio 5 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_b"; + }; + }; +/* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up temp */ + i2c@f9926000 { + pn547@28 { + compatible = "nxp,pn547"; + status = "ok"; + reg = <0x28>; + interrupt-parent = <&msmgpio>; + interrupts = <99 0x2>; + nxp,gpio_sda = <&msmgpio 88 0x00>; + nxp,gpio_scl = <&msmgpio 89 0x00>; + nxp,gpio_ven = <&msmgpio 71 0x00>; + nxp,gpio_mode = <&msmgpio 70 0x00>; + nxp,gpio_irq = <&msmgpio 99 0x00>; + nxp,i2c-pull-up = <1>; + revision = "rev_c..."; + }; + }; + + +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-panel.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-panel.dtsi new file mode 100644 index 000000000000..84ef2af28ec9 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-panel.dtsi @@ -0,0 +1,45 @@ +/* Copyright (c) 2013, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_lgd_lg4577_wvga_video>; + qcom,dsi-pref-secondary-pan = <&dsi_tovis_shrink_wvga_video>; + qcom,platform-supply-entry3 { + qcom,supply-name = "vdda"; + qcom,supply-min-voltage = <2850000>; + qcom,supply-max-voltage = <2850000>; + qcom,supply-enable-load = <100000>; + qcom,supply-disable-load = <100>; + qcom,supply-pre-on-sleep = <0>; + qcom,supply-post-on-sleep = <5>; + qcom,supply-pre-off-sleep = <0>; + qcom,supply-post-off-sleep = <0>; + }; +}; + +&dsi_tovis_shrink_wvga_video { + qcom,cont-splash-enabled; + status = "disable"; + revision = "rev_b..."; +}; + +&dsi_lgd_lg4577_wvga_video { + qcom,cont-splash-enabled; + status = "disable"; + revision = "rev_a"; +}; + diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-pm.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-pm.dtsi new file mode 100644 index 000000000000..337f6ce304df --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-pm.dtsi @@ -0,0 +1,299 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +/ { + +}; + +&spmi_bus { + qcom,pm8110@0 { + qcom,leds@a100 { + status = "okay"; + qcom,led_mpp_2 { + label = "mpp"; + linux,name = "button-backlight"; + linux-default-trigger = "hr-trigger"; + qcom,default-state = "off"; + qcom,max-current = <40>; + qcom,id = <6>; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x60>; + qcom,mode = "manual"; + }; + }; + + qcom,leds@a200 { + status = "disabled"; + qcom,led_mpp_3 { + label = "mpp"; + linux,name = "wled-backlight"; + linux,default-trigger = "bkl-trigger"; + qcom,default-state = "on"; + qcom,max-current = <40>; + qcom,id = <6>; + qcom,source-sel = <1>; + qcom,mode-ctrl = <0x10>; + qcom,mode = "manual"; + }; + }; + }; +}; + +&spmi_bus { + qcom,pm8110@1 { + qcom,vibrator@c000 { + status = "okay"; + qcom,vib-timeout-ms = <15000>; + qcom,vib-vtg-level-mV = <3000>; + }; + }; +}; + +&soc{ + /* Cable type definition. + * Modify the USB cable and move to SoC + * to get each table for different USB Pull up resistor via HW Rev + * This must have the compatible property "lge,usb-id". + * If you don't want to use revision you can remove status & revision property. + * If this node is not exist, the charger driver would not be probed. + * Each cable type has 3 values + * that we can configure the threshold of ADC value and TA/USB IUSB max current value. + * ex) + * cable type = . + */ + + lge,usb_cable@0 { + compatible = "lge,usb-id"; + usb_adc_table@665{ + status = "okay"; + revision = "rev_0"; + lge,no-init-cable = <100000 500 500>; + lge,cable-56k = <200000 1500 1500>; + lge,cable-100k = <239000 500 500>; + lge,cable-130k = <340000 1500 1500>; + lge,cable-180k = <400000 800 500>; + lge,cable-200k = <430000 800 500>; + lge,cable-220k = <485000 800 500>; + lge,cable-270k = <560000 800 500>; + lge,cable-330k = <735000 500 500>; + lge,cable-620k = <955000 500 500>; + lge,cable-910k = <1140000 1500 1500>; + lge,cable-none = <1900000 800 500>; + }; + usb_adc_table@200{ + status = "okay"; + revision = "rev_a..."; + lge,no-init-cable = <197000 500 500>; + lge,cable-56k = <496875 1500 1500>; + lge,cable-100k = <654545 500 500>; + lge,cable-130k = <780861 1500 1500>; + lge,cable-180k = <876316 800 500>; + lge,cable-200k = <921429 800 500>; + lge,cable-220k = <988450 800 500>; + lge,cable-270k = <1077399 800 500>; + lge,cable-330k = <1240865 500 500>; + lge,cable-620k = <1418326 500 500>; + lge,cable-910k = <1637838 1500 1500>; + lge,cable-none = <1900000 800 500>; + }; + }; +}; + +&pm8110_chg { + status = "ok"; + qcom,vddmax-mv = <4350>; + qcom,vddsafe-mv = <4380>; + qcom,vbatdet-mv = <4300>; + qcom,vinmin-mv = <4350>; + qcom,ibatterm-ma = <100>; + qcom,vbatdet-delta-mv = <100>; + qcom,tchg-mins = <480>; + qcom,duty-cycle-100p = <1>; + + qcom,chgr@1000 { + status = "ok"; + }; + + qcom,buck@1100 { + status = "ok"; + }; + + qcom,bat-if@1200 { + status = "ok"; + }; + + qcom,usb-chgpth@1300 { + status = "ok"; + }; + + qcom,chg-misc@1600 { + status = "ok"; + }; +}; + +&pm8110_gpios { + gpio@c000 { /* GPIO 1 */ /* NFC_CLK_REQ */ + }; + + gpio@c100 { /* GPIO 2 */ /* TX_GTR_THRESH */ + qcom,mode = <1>; /* QPNP_PIN_MODE_DIG_OUT */ + qcom,output-type = <0>; /* QPNP_PIN_OUT_BUF_CMOS */ + qcom,pull = <5>; /* QPNP_PIN_PULL_NO */ + qcom,vin-sel = <0>; /* QPNP_PIN_VIN0 */ + qcom,out-strength = <1>; /* QPNP_PIN_OUT_STRENGTH_LOW */ + qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; /* Eable GPIO */ + }; + + gpio@c200 { /* GPIO 3 */ /* NC */ + }; + + gpio@c300 { /* GPIO 4 */ /* BAT_UICC_ALARM */ + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + /* GPIO 59 : EAR_MIC_EN */ + gpio@fa00 { + status = "ok"; + qcom,mode = <1>; + qcom,output-type = <0>; + qcom,pull = <1>; /* set to default pull */ + qcom,out-strength = <2>; + com,vin-sel = <2>; /* select 1.8 V source */ + qcom,src-select = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ +}; + +&pm8110_mpps { + mpp@a000 { /* MPP 1 */ /* VDD_PX_BIAS */ + }; + + mpp@a100 { /* MPP 2 */ /* VPWR_KEY_LED */ +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + reg = <0xa100 0x100>; + qcom,pin-num = <2>; + + status = "ok"; + qcom,mode = <4>; /* QPNP_PIN_MODE_AIN */ + qcom,invert = <0>; /*no invert*/ + qcom,output-type = <0>; /* CMOS */ + qcom,vin-sel = <2>; /* PM8226_S3 1.8V > 1.6V */ + qcom,src-sel = <0>; /* CONSTANT */ + qcom,ain-route = <1>; + qcom,master-en = <1>; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ + }; + + mpp@a200 { /* MPP 3 */ /* USB_ID */ + qcom,mode = <4>; /* QPNP_PIN_MODE_AIN */ + qcom,invert = <1>; /* QPNP_PIN_INVERT_ENABLE - Enable MPP */ + qcom,src-sel = <0>; /* QPNP_PIN_SEL_FUNC_CONSTANT */ + qcom,master-en = <1>; /* Eable GPIO */ + qcom,ain-route = <2>; /* QPNP_PIN_AIN_AMUX_CH7 */ + }; + + mpp@a300 { /* MPP 4 */ /* PA_THERM */ + /* PA_THERM config */ + qcom,mode = <4>; /* AIN input */ + qcom,invert = <1>; /* Enable MPP */ + qcom,ain-route = <3>; /* AMUX 8 */ + qcom,master-en = <1>; + qcom,src-sel = <0>; /* Function constant */ + }; +}; + +&pm8110_bms { + status = "ok"; + + qcom,r-sense-uohm = <10000>; + qcom,v-cutoff-uv = <3400000>; + qcom,max-voltage-uv = <4350000>; + qcom,r-conn-mohm = <18>; + qcom,shutdown-soc-valid-limit = <20>; + qcom,adjust-soc-low-threshold = <25>; + qcom,adjust-soc-high-threshold = <45>; + qcom,ocv-voltage-high-threshold-uv = <3900000>; + qcom,ocv-voltage-low-threshold-uv = <3650000>; + qcom,low-soc-calculate-soc-threshold = <15>; + qcom,low-soc-calculate-soc-ms = <5000>; + qcom,calculate-soc-ms = <20000>; + qcom,low-voltage-calculate-soc-ms=<1000>; + qcom,chg-term-ua = <100000>; + qcom,batt-type = <0>; + qcom,low-voltage-threshold = <3420000>; +}; + +&pm8110_vadc { + +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [Start] */ + chan@11 { + status = "ok"; + label = "mpp2_adc"; + reg = <0x11>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; +/* 2013-08-08, seungkyu.joo ,Maxim Detector Button Setting [End] */ + + chan@12 { + label = "mpp3_usb_id"; + reg = <0x12>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + + chan@13 { + label = "pa_therm0"; + reg = <0x13>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "ratiometric"; + qcom,scale-function = <2>; /* degC for 100k pull-up */ + qcom,hw-settle-time = <2>; + qcom,fast-avg-setup = <0>; + }; + + chan@33 { + label = "hw_id_pcb_revision"; + reg = <0x33>; + qcom,decimation = <0>; + qcom,pre-div-channel-scaling = <0>; + qcom,calibration-type = "absolute"; + qcom,scale-function = <0>; + qcom,hw-settle-time = <0>; + qcom,fast-avg-setup = <0>; + }; + +}; + +&rpm_bus { + + rpm-regulator-ldoa7 { + status = "okay"; + pm8110_l7: regulator-l7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,init-voltage = <1800000>; + status = "okay"; + }; + }; +}; + diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-sensor.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-sensor.dtsi new file mode 100644 index 000000000000..dbcd78e1427f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-sensor.dtsi @@ -0,0 +1,139 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +&soc { + /* I2C device */ + i2c@f9925000 { + /* Acceleration sensor */ + + K303C_ACC@1D { + revision = "rev_a"; + status = "okay"; + compatible = "STMicroelectronics,accelerometer" , "STMicroelectronics,K303C" ; + reg = <0x1D>; + interrupt-parent = <&msmgpio>; + interrupts = <81 0x2>; + vdda-supply = <&pm8110_l19>; + vddio-supply = <&pm8110_l14>; + STMicroelectronics,irq-gpio = <&msmgpio 81 0x00>; + STMicroelectronics,i2c-pull-up = <1>; + STMicroelectronics,vdd_ana_supply_min = <2850000>; + STMicroelectronics,vdd_ana_supply_max = <3300000>; + STMicroelectronics,vdd_ana_load_ua = <15000>; + STMicroelectronics,vddio_dig_supply_min = <1800000>; + STMicroelectronics,vddio_dig_supply_max = <1800000>; + STMicroelectronics,vddio_dig_load_ua = <10000>; + STMicroelectronics,vddio_i2c_supply_min = <1800000>; + STMicroelectronics,vddio_i2c_supply_max = <1800000>; + STMicroelectronics,vddio_i2c_load_ua = <10000>; + }; + + + Bosch_bma2x2@10 { + compatible = "Bosch,bma2x2"; + reg = <0x10>; + interrupt-parent = <&msmgpio>; + interrupts = <81 0x2>; + Bosch,vdd_ana-supply = <&pm8110_l19>; + Bosch,vddio_i2c-supply = <&pm8110_l14>; + Bosch,irq-gpio = <&msmgpio 81 0x00>; + Bosch,i2c-pull-up = <1>; + Bosch,vdd_ana_supply_min = <2850000>; + Bosch,vdd_ana_supply_max = <3300000>; + Bosch,vdd_ana_load_ua = <15000>; + Bosch,vddio_dig_supply_min = <1800000>; + Bosch,vddio_dig_supply_max = <1800000>; + Bosch,vddio_dig_load_ua = <10000>; + Bosch,vddio_i2c_supply_min = <1800000>; + Bosch,vddio_i2c_supply_max = <1800000>; + Bosch,vddio_i2c_load_ua = <10000>; + place = <4>; + status = "ok"; + revision = "rev_b..."; + }; + + /* Magnetic Sensor Driver */ + K303C_MAG@1E { + revision = "rev_a"; + status = "okay"; + compatible = "STMicroelectronics,magnetometer" , "STMicroelectronics,K303C"; + reg = <0x1E>; + interrupt-parent = <&msmgpio>; + interrupts = <83 0x2>; + vdda-supply = <&pm8110_l19>; + vddio-supply = <&pm8110_l14>; + STMicroelectronics,irq-gpio = <&msmgpio 83 0x00>; + STMicroelectronics,i2c-pull-up = <1>; + STMicroelectronics,vdd_ana_supply_min = <2850000>; + STMicroelectronics,vdd_ana_supply_max = <3300000>; + STMicroelectronics,vdd_ana_load_ua = <15000>; + STMicroelectronics,vddio_dig_supply_min = <1800000>; + STMicroelectronics,vddio_dig_supply_max = <1800000>; + STMicroelectronics,vddio_dig_load_ua = <10000>; + STMicroelectronics,vddio_i2c_supply_min = <1800000>; + STMicroelectronics,vddio_i2c_supply_max = <1800000>; + STMicroelectronics,vddio_i2c_load_ua = <10000>; + + }; + + Bosch_bmm050@12 { + compatible = "Bosch,bmm050"; + reg = <0x12>; + interrupt-parent = <&msmgpio>; + interrupts = <83 0x2>; + Bosch,vdd_ana-supply = <&pm8110_l19>; + Bosch,vddio_i2c-supply = <&pm8110_l14>; + Bosch,irq-gpio = <&msmgpio 83 0x00>; + Bosch,i2c-pull-up = <1>; + Bosch,vdd_ana_supply_min = <2850000>; + Bosch,vdd_ana_supply_max = <3300000>; + Bosch,vdd_ana_load_ua = <15000>; + Bosch,vddio_dig_supply_min = <1800000>; + Bosch,vddio_dig_supply_max = <1800000>; + Bosch,vddio_dig_load_ua = <10000>; + Bosch,vddio_i2c_supply_min = <1800000>; + Bosch,vddio_i2c_supply_max = <1800000>; + Bosch,vddio_i2c_load_ua = <10000>; + place = <4>; + status = "ok"; + revision = "rev_b..."; + }; + + /* Proximity sensor */ + Avago_apds9130@39 { + compatible = "Avago,apds9130"; + reg = <0x39>; + interrupt-parent = <&msmgpio>; + interrupts = <80 0x2>; + Avago,vdd_ana-supply = <&pm8110_l19>; + Avago,vddio_i2c-supply = <&pm8110_l14>; + Avago,irq-gpio = <&msmgpio 80 0x00>; + Avago,i2c-pull-up = <1>; + Avago,vdd_ana_supply_min = <2850000>; + Avago,vdd_ana_supply_max = <3300000>; + Avago,vdd_ana_load_ua = <15000>; + Avago,vddio_dig_supply_min = <1800000>; + Avago,vddio_dig_supply_max = <1800000>; + Avago,vddio_dig_load_ua = <10000>; + Avago,vddio_i2c_supply_min = <1800000>; + Avago,vddio_i2c_supply_max = <1800000>; + Avago,vddio_i2c_load_ua = <10000>; + /*add*/ + Avago,ppcount = <8>; + Avago,pdrive = <0>; + Avago,near_offset = <150>; + Avago,far_offset = <50>; + Avago,crosstalk_max = <770>; + status = "ok"; + }; + }; +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-usb.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-usb.dtsi new file mode 100644 index 000000000000..44d938008f8f --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-usb.dtsi @@ -0,0 +1,15 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +/ { + +}; diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n.dtsi new file mode 100644 index 000000000000..946ec03d9d77 --- /dev/null +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n.dtsi @@ -0,0 +1,156 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/include/ "msm8610-w5n-panel.dtsi" +/include/ "msm8610-w5n-sensor.dtsi" +/include/ "msm8610-w5n-hdmi.dtsi" +/include/ "msm8610-w5n-usb.dtsi" +/include/ "msm8610-w5n-input.dtsi" +/include/ "msm8610-w5n-misc.dtsi" +/include/ "msm8610-w5n-pm.dtsi" +/include/ "msm8610-w5n-camera.dtsi" +/include/ "msm8610-w5n-nfc.dtsi" /* LGE_CHANGE, [NFC][taesik.kim@lge.com], NFC Bring up */ + +&soc { + serial@f991f000 { + status = "disabled"; + }; + + maxim_max1462x_rev0 { //LGE_UPDATE 20130626 beekay.lee@lge.com WX_MAXIM + compatible = "maxim,max1462x"; + status = "ok"; //"ok" or "disable" + revision = "rev_0"; + }; + + maxim_max1462x_revA { + compatible = "maxim,max1462x"; + status = "ok"; //"ok" or "disable" + revision = "rev_a"; + }; + + maxim_max1462x_revB { + compatible = "maxim,max1462x"; + status = "disable"; //"ok" or "disable" + revision = "rev_b"; + }; + + maxim_max1462x_revC { + compatible = "maxim,max1462x"; + status = "disable"; //"ok" or "disable" + revision = "rev_c..."; + }; + + i2c@f9924000 { /* BLSP-1 QUP-2 */ + cell-index = <2>; + compatible = "qcom,i2c-qup"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0xf9924000 0x1000>; + interrupt-names = "qup_err_intr"; + interrupts = <0 96 0>; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <19200000>; + qcom,sda-gpio = <&msmgpio 6 0>; + qcom,scl-gpio = <&msmgpio 7 0>; + status = "disable"; + }; + + i2c@f9925000 { /* BLSP-1 QUP-3 */ + cell-index = <3>; + qcom,i2c-src-freq = <19200000>; + qcom,sda-gpio = <&msmgpio 10 0>; + qcom,scl-gpio = <&msmgpio 11 0>; + }; + + i2c@0 { + compatible = "i2c-gpio"; + gpios = <&msmgpio 4 0 /* sda */ + &msmgpio 5 0 /* scl */ + >; + #address-cells = <1>; + #size-cells = <0>; + i2c-gpio,delay-us = <2>; + }; + i2c@f9926000 { /* BLSP1 QUP4 */ + cell-index = <4>; + compatible = "qcom,i2c-qup"; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "qup_phys_addr"; + reg = <0xf9926000 0x1000>; + interrupt-names = "qup_err_intr"; + interrupts = <0 98 0>; + qcom,i2c-bus-freq = <100000>; + qcom,i2c-src-freq = <19200000>; + revision = "rev_c..."; + }; +}; + +&sdhc_1 { + vdd-supply = <&pm8110_l17>; + qcom,vdd-always-on; + qcom,vdd-lpm-sup; + qcom,vdd-voltage-level = <2900000 2900000>; + qcom,vdd-current-level = <200 400000>; + + vdd-io-supply = <&pm8110_l6>; + qcom,vdd-io-always-on; + qcom,vdd-io-voltage-level = <1800000 1800000>; + qcom,vdd-io-current-level = <200 60000>; + + qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */ + qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ + + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v"; + qcom,nonremovable; + + status = "ok"; +}; + +&sdhc_2 { + vdd-supply = <&pm8110_l18>; + qcom,vdd-voltage-level = <2950000 2950000>; + qcom,vdd-current-level = <15000 400000>; + + vdd-io-supply = <&pm8110_l21>; + qcom,vdd-io-voltage-level = <1800000 2950000>; + qcom,vdd-io-current-level = <200 50000>; + + qcom,pad-pull-on = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */ + qcom,pad-drv-on = <0x4 0x4 0x4>; /* 10mA, 10mA, 10mA */ + qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ + + qcom,clk-rates = <400000 25000000 50000000 100000000 200000000>; + + #address-cells = <0>; + interrupt-parent = <&sdhc_2>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 125 0 + 1 &intc 0 221 0 + 2 &msmgpio 42 0x3>; + interrupt-names = "hc_irq", "pwr_irq", "status_irq"; + cd-gpios = <&msmgpio 42 0x0>; /* card detect 1:Low-Active, 0:High-Active */ + + status = "ok"; +}; +/* this memory reservation setting for LG logo in booting time*/ +&mdss_fb0 { + qcom,memory-reservation-type = "EBI1"; + qcom,memory-reservation-size = <0x300000>; +}; diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig new file mode 100644 index 000000000000..842ab6c5c704 --- /dev/null +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -0,0 +1,522 @@ +# CONFIG_ARM_PATCH_PHYS_VIRT is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_AUDIT=y +CONFIG_RCU_FAST_NO_HZ=y +CONFIG_IKCONFIG=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=m +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_IOSCHED_TEST=y +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM8610=y +CONFIG_MACH_MSM8X10_W5=y +CONFIG_MACH_MSM8X10_W5N_GLOBAL_COM=y +CONFIG_LGE_BOOTLOADER_LOG=y +CONFIG_LGE_HANDLE_PANIC=y +CONFIG_LGE_BOOTLOADER_TIME_CHECKER=y +CONFIG_LGE_USE_CPU_CLOCK_TIMESTAMP=y +CONFIG_LGE_QFPROM_INTERFACE=y +CONFIG_LGE_PM_BATTERY_CAPACITY_2100mAh=y +CONFIG_LGE_CRASH_FOOTPRINT=y +# CONFIG_MSM_STACKED_MEMORY is not set +CONFIG_CPU_HAS_L2_PMU=y +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_PROC_COMM is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_SMD_PKG4=y +CONFIG_MSM_BAM_DMUX=y +CONFIG_MSM_SMP2P=y +CONFIG_MSM_SMP2P_TEST=y +CONFIG_MSM_IPC_LOGGING=y +CONFIG_MSM_IPC_ROUTER=y +CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y +CONFIG_MSM_QMI_INTERFACE=y +CONFIG_MSM_SUBSYSTEM_RESTART=y +CONFIG_MSM_SYSMON_COMM=y +CONFIG_MSM_PIL_LPASS_QDSP6V5=y +CONFIG_MSM_PIL_MSS_QDSP6V5=y +CONFIG_MSM_PIL_VENUS=y +CONFIG_MSM_PIL_PRONTO=y +CONFIG_MSM_BUSPM_DEV=m +CONFIG_MSM_TZ_LOG=y +CONFIG_MSM_RPM_RBCPR_STATS_V2_LOG=y +CONFIG_MSM_DIRECT_SCLK_ACCESS=y +CONFIG_MSM_WATCHDOG_V2=y +CONFIG_MSM_MEMORY_DUMP=y +CONFIG_MSM_DLOAD_MODE=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_MSM_OCMEM=y +CONFIG_MSM_OCMEM_LOCAL_POWER_CTRL=y +CONFIG_MSM_OCMEM_DEBUG=y +CONFIG_MSM_OCMEM_NONSECURE=y +CONFIG_MSM_OCMEM_POWER_DISABLE=y +CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y +CONFIG_MSM_BOOT_STATS=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_SMP=y +CONFIG_SCHED_MC=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_COMPACTION=y +CONFIG_KSM=y +CONFIG_CP_ACCESS=y +CONFIG_USE_OF=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_RUNTIME=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_XFRM_SUB_POLICY=y +CONFIG_XFRM_MIGRATE=y +CONFIG_XFRM_STATISTICS=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_LOG=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE_NF_EBTABLES=y +CONFIG_BRIDGE_EBT_BROUTE=y +CONFIG_L2TP=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=y +CONFIG_NET_SCH_PRIO=y +CONFIG_NET_SCH_MULTIQ=y +CONFIG_NET_SCH_RED=y +CONFIG_NET_SCH_SFB=y +CONFIG_NET_SCH_SFQ=y +CONFIG_NET_SCH_TEQL=y +CONFIG_NET_SCH_TBF=y +CONFIG_NET_SCH_GRED=y +CONFIG_NET_SCH_DSMARK=y +CONFIG_NET_SCH_NETEM=y +CONFIG_NET_SCH_DRR=y +CONFIG_NET_SCH_MQPRIO=y +CONFIG_NET_SCH_CHOKE=y +CONFIG_NET_SCH_QFQ=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_ACT=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCISMD=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +CONFIG_LGE_NFC=y +CONFIG_LGE_NFC_PN547=y +CONFIG_LGE_NFC_SET_IRQ_WAKEUP=y +CONFIG_CMA=y +CONFIG_CMA_SIZE_MBYTES=4 +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_UID_STAT=y +CONFIG_TSPDRV=y +CONFIG_TSPDRV_3_0V_VIBRATOR=y +CONFIG_QSEECOM=y +CONFIG_BU52061NVX=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_TUN=y +# CONFIG_MSM_RMNET is not set +CONFIG_MSM_RMNET_BAM=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=y +CONFIG_PPPOL2TP=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_WCNSS_CORE=y +CONFIG_WCNSS_CORE_PRONTO=y +CONFIG_WCNSS_MEM_PRE_ALLOC=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_EVBUG=m +CONFIG_KEYBOARD_GPIO=y +CONFIG_INPUT_SENSOR=y +CONFIG_SENSORS_BMA2X2=y +CONFIG_SENSORS_BMM050=y +CONFIG_SENSOR_APDS9130=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_GEN_VKEYS=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI4_DEV=y +CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE=y +CONFIG_TOUCHSCREEN_APS_MELFAS=y +CONFIG_TOUCHSCREEN_ADS_AGS04=y +CONFIG_LGE_TOUCHSCREEN_SYNAPTIC=y +CONFIG_TOUCHSCREEN_MELFAS_MMS100S=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=m +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_N_HDLC=y +CONFIG_SERIAL_MSM_HSL=y +CONFIG_SERIAL_MSM_HSL_CONSOLE=y +CONFIG_LGE_DEBUG_UART=y +CONFIG_DIAG_CHAR=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MSM=y +CONFIG_MSM_ADSPRPC=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_QUP=y +CONFIG_SPI=y +CONFIG_SPI_QUP=y +CONFIG_SPI_SPIDEV=m +CONFIG_SPMI=y +CONFIG_SPMI_MSM_PMIC_ARB=y +CONFIG_MSM_QPNP_INT=y +CONFIG_SLIMBUS_MSM_NGD=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_QPNP_PIN=y +CONFIG_POWER_SUPPLY=y +CONFIG_QPNP_CHARGER=y +CONFIG_BATTERY_BCL=y +CONFIG_QPNP_BMS=y +CONFIG_LGE_PM=y +CONFIG_LGE_PM_BATTERY_ID_CHECKER=y +CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY=y +CONFIG_LGE_PM_FACTORY_TESTMODE=y +CONFIG_LGE_PM_USB_ID=y +CONFIG_LGE_PM_CHARGING_CHARGERLOGO=y +CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO=y +CONFIG_LGE_PM_BATTERY_PROFILE_DATA=y +CONFIG_LGE_PM_BATTERY_SOC_RESCALING=y +CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME=y +CONFIG_LGE_PM_BMS_VERY_LOW_CAL_TIME=y +CONFIG_LGE_PM_NEED_TO_MONITORING_QCT_PATCH=y +CONFIG_LGE_PM_4_25V_CHARGING_START=y +CONFIG_LGE_PM_SMPL_COUNT=y +CONFIG_LGE_PM_THERMAL=y +CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO=y +CONFIG_LGE_PM_WORKAROUND_PORT_OPEN_FAIL_IN_FACTORY_TEST=y +CONFIG_LGE_PM_BMS_FACTORY_TEST=y +CONFIG_LGE_PM_BMS_CC_ACCURACY_PATCH=y +CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST=y +CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y +CONFIG_SENSORS_QPNP_ADC_CURRENT=y +CONFIG_THERMAL=y +CONFIG_THERMAL_TSENS8974=y +CONFIG_THERMAL_MONITOR=y +CONFIG_THERMAL_QPNP=y +CONFIG_THERMAL_QPNP_ADC_TM=y +CONFIG_WCD9306_CODEC=y +CONFIG_REGULATOR_STUB=y +CONFIG_REGULATOR_QPNP=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +# CONFIG_MSM_CAMERA is not set +CONFIG_MSMB_CAMERA=y +CONFIG_MSM_CAMERA_SENSOR=y +CONFIG_MSM_CCI=y +CONFIG_MSM_CSI22_HEADER=y +CONFIG_MSM_CSIPHY=y +CONFIG_MSM_CSID=y +CONFIG_MSM_EEPROM=y +CONFIG_MSM_ISPIF=y +CONFIG_MSM_ISPIF_V1=y +CONFIG_IMX111=y +CONFIG_IMX219=y +CONFIG_HI543=y +CONFIG_HI707=y +CONFIG_BACKLIGHT_RT8542=y +CONFIG_MSM_VIDC_V4L2=y +CONFIG_MSM_WFD=y +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2063 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_XC4000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set +# CONFIG_MEDIA_TUNER_TDA18212 is not set +CONFIG_VIDEOBUF2_MSM_MEM=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_RADIO_IRIS=y +CONFIG_RADIO_IRIS_TRANSPORT=m +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y +CONFIG_FB=y +CONFIG_FB_MSM=y +# CONFIG_FB_MSM_BACKLIGHT is not set +CONFIG_FB_MSM_MDSS=y +CONFIG_FB_MSM_MDSS_WRITEBACK=y +CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MSM8X10=y +CONFIG_UHID=y +CONFIG_HID_APPLE=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DEBUG_FILES=y +CONFIG_USB_GADGET_DEBUG_FS=y +CONFIG_USB_CI13XXX_MSM=y +CONFIG_USB_G_ANDROID=y +CONFIG_LAF_G_DRIVER=y +CONFIG_USB_G_LGE_ANDROID_PFSC=y +CONFIG_USB_G_LGE_ANDROID_AUTORUN=y +CONFIG_USB_G_LGE_ANDROID_AUTORUN_LGE=y +CONFIG_MMC=y +CONFIG_MMC_PERF_PROFILING=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=m +CONFIG_MMC_BLOCK_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_MSM=y +CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_MSM_SPS_SUPPORT=y +CONFIG_LEDS_QPNP=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_SWITCH=y +CONFIG_SWITCH_MAX1462X=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_MSM is not set +CONFIG_RTC_DRV_QPNP=y +CONFIG_UIO=y +CONFIG_UIO_MSM_SHAREDMEM=y +CONFIG_STAGING=y +CONFIG_ZRAM=y +CONFIG_ZSMALLOC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_LOGCAT_SIZE=64 +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_PRONTO_WLAN=y +CONFIG_PRIMA_WLAN_LFR=y +CONFIG_PRIMA_WLAN_OKC=y +CONFIG_PRIMA_WLAN_11AC_HIGH_TP=y +CONFIG_WLAN_FEATURE_11W=y +CONFIG_QCOM_VOWIFI_11R=y +CONFIG_SPS=y +CONFIG_USB_BAM=y +CONFIG_SPS_SUPPORT_NDP_BAM=y +CONFIG_QPNP_PWM=y +CONFIG_QPNP_POWER_ON=y +CONFIG_QPNP_VIBRATOR=y +CONFIG_QPNP_REVID=y +CONFIG_MSM_IOMMU_V0=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_FUSE_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_ECRYPT_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_SET_MODULE_RONX=y +CONFIG_KEYS=y +CONFIG_ENCRYPTED_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_LSM_MMAP_MIN_ADDR=4096 +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_HW is not set +CONFIG_LGE_DM_APP=y +CONFIG_LGE_DM_DEV=y From 1a6fbef8ac10bbce1811beea14f8a73843528bc2 Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 26 Mar 2015 16:07:08 +0300 Subject: [PATCH 052/104] Add interactive governor and enable by defaul --- arch/arm/configs/w3ds_global_com_defconfig | 2 ++ arch/arm/configs/w5_global_com_defconfig | 2 ++ arch/arm/configs/w5ds_global_com_defconfig | 2 ++ arch/arm/configs/w5n_global_com_defconfig | 2 ++ 4 files changed, 8 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index 3f4ba8114905..dcb140de38b0 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -95,6 +95,8 @@ CONFIG_USE_OF=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig index 9bbf9bd2090f..a1794d8e0044 100644 --- a/arch/arm/configs/w5_global_com_defconfig +++ b/arch/arm/configs/w5_global_com_defconfig @@ -94,6 +94,8 @@ CONFIG_USE_OF=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index f7acfba64e26..7f910093f88a 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -94,6 +94,8 @@ CONFIG_USE_OF=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 842ab6c5c704..453643fac8ee 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -94,6 +94,8 @@ CONFIG_USE_OF=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_NEON=y From 9fe6f2057c12b96b0dd62b2a8127d97086dfa3a0 Mon Sep 17 00:00:00 2001 From: vm03 Date: Thu, 26 Mar 2015 17:20:06 +0300 Subject: [PATCH 053/104] Add PERFORMANCE governor --- arch/arm/configs/w3ds_global_com_defconfig | 1 + arch/arm/configs/w5_global_com_defconfig | 1 + arch/arm/configs/w5ds_global_com_defconfig | 1 + arch/arm/configs/w5n_global_com_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index dcb140de38b0..acaf8bb08153 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -96,6 +96,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig index a1794d8e0044..8dbc92b75da1 100644 --- a/arch/arm/configs/w5_global_com_defconfig +++ b/arch/arm/configs/w5_global_com_defconfig @@ -95,6 +95,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index 7f910093f88a..d13e08a20893 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -95,6 +95,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 453643fac8ee..392c518e7ffa 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -95,6 +95,7 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y CONFIG_CPU_IDLE=y CONFIG_VFP=y From a4ec72c1cfa142d205467d7a4f6d91bbe750c7d1 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 29 Mar 2015 00:30:12 +0300 Subject: [PATCH 054/104] dts: more keys for w3ds --- .../boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi index a4ea90436d4f..fa61105499b5 100644 --- a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-input.dtsi @@ -49,7 +49,7 @@ synaptics_red@20 { status = "disable"; revision = "rev_d..."; - synaptics,button-map = <158 139>; + synaptics,button-map = <158 172 139 249>; synaptics,gpio_vdd_en = <82>; }; @@ -64,8 +64,8 @@ synaptics,global_access_pixel = <10>; lge,knock_on_type = <1>; synaptics,platform_data { - number_of_button = <2>; - button_name = [9e 8b]; + number_of_button = <4>; + button_name = [9e ac 8b f9]; x_max = <960>; y_max = <1600>; lcd_x = <480>; From 8f234cfe2aa00666206e737b567a804cd9e826db Mon Sep 17 00:00:00 2001 From: Chris Fries Date: Fri, 5 Dec 2014 16:07:41 -0600 Subject: [PATCH 055/104] ARM: Use -mtune=cortex-a7 for 8226/8610 Found a 3% improvement in zram speed using cortex-a7 tuning (these chipsets use Cortex A7). Change-Id: I22c34f2f17c5908c50bf019b602bf770ca83bf32 Signed-off-by: Chris Fries Reviewed-on: http://gerrit.mot.com/695723 Tested-by: Jira Key Reviewed-by: Igor Kovalenko Reviewed-by: Russell Knize Submit-Approved: Jira Key (cherry picked from commit 40ab53191728cb4484a7c33276aeadd3fd78fc23) --- arch/arm/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 4b544b71f355..738cc6c91305 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -102,6 +102,8 @@ tune-$(CONFIG_CPU_XSC3) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) - tune-$(CONFIG_CPU_FEROCEON) :=$(call cc-option,-mtune=marvell-f,-mtune=xscale) tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) tune-$(CONFIG_CPU_V6K) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) +tune-$(CONFIG_ARCH_MSM8226) :=-mtune=cortex-a7 +tune-$(CONFIG_ARCH_MSM8610) :=-mtune=cortex-a7 ifeq ($(CONFIG_AEABI),y) CFLAGS_ABI :=-mabi=aapcs-linux -mno-thumb-interwork From f91645e24d75152b6752d598b8b05410f2c66096 Mon Sep 17 00:00:00 2001 From: Chris Fries Date: Wed, 17 Dec 2014 10:04:08 -0600 Subject: [PATCH 056/104] ARM: use -mcpu=cortex-a7 to get integer divide instruction Use -mcpu=cortex-a7 for MSM8226, MSM8610 in order to enable compiler to use udiv/sdiv instructions. Change-Id: I061b2a1b7c5cdbe1d2bd07beca34a05d7e6e2efe Signed-off-by: Chris Fries Reviewed-on: http://gerrit.mot.com/699266 Tested-by: Jira Key Reviewed-by: Russell Knize Submit-Approved: Jira Key (cherry picked from commit 0cb2bac91e79a64efef500e55ee37ce684e46807) --- arch/arm/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 738cc6c91305..81692a82b1d5 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -80,6 +80,8 @@ arch-$(CONFIG_CPU_32v3) :=-D__LINUX_ARM_ARCH__=3 -march=armv3 # doesn't even support -march=armv7-a, since in that situation we would have # bigger problems. arch-$(CONFIG_ARCH_MSM_KRAIT) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-mcpu=cortex-a15,-march=armv7-a) +arch-$(CONFIG_ARCH_MSM8226) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-mcpu=cortex-a7,-march=armv7-a) +arch-$(CONFIG_ARCH_MSM8610) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-mcpu=cortex-a7,-march=armv7-a) # This selects how we optimise for the processor. tune-$(CONFIG_CPU_ARM610) :=-mtune=arm610 From 12bff2e4f41d3466a0007ccdcd54ddfe8b53cba1 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Fri, 20 Dec 2013 15:10:03 +0200 Subject: [PATCH 057/104] mm: Fix NULL pointer dereference in madvise(MADV_WILLNEED) support Sasha Levin found a NULL pointer dereference that is due to a missing page table lock, which in turn is due to the pmd entry in question being a transparent huge-table entry. The code - introduced in commit 1998cc048901 ("mm: make madvise(MADV_WILLNEED) support swap file prefetch") - correctly checks for this situation using pmd_none_or_trans_huge_or_clear_bad(), but it turns out that that function doesn't work correctly. pmd_none_or_trans_huge_or_clear_bad() expected that pmd_bad() would trigger if the transparent hugepage bit was set, but it doesn't do that if pmd_numa() is also set. Note that the NUMA bit only gets set on real NUMA machines, so people trying to reproduce this on most normal development systems would never actually trigger this. Fix it by removing the very subtle (and subtly incorrect) expectation, and instead just checking pmd_trans_huge() explicitly. Reported-by: Sasha Levin Acked-by: Andrea Arcangeli [ Additionally remove the now stale test for pmd_trans_huge() inside the pmd_bad() case - Linus ] Signed-off-by: Linus Torvalds Change-Id: I3f3763f236ef102de735297cd175cf514d40d28f --- include/asm-generic/pgtable.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 125c54e98517..6c449f203b9e 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -471,11 +471,10 @@ static inline int pmd_none_or_trans_huge_or_clear_bad(pmd_t *pmd) #ifdef CONFIG_TRANSPARENT_HUGEPAGE barrier(); #endif - if (pmd_none(pmdval)) + if (pmd_none(pmdval) || pmd_trans_huge(pmdval)) return 1; if (unlikely(pmd_bad(pmdval))) { - if (!pmd_trans_huge(pmdval)) - pmd_clear_bad(pmd); + pmd_clear_bad(pmd); return 1; } return 0; From c14a882da71d6313e8b04772cd978a63e1068390 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 28 Jan 2015 20:30:43 -0600 Subject: [PATCH 058/104] vfs: read file_handle only once in handle_to_path We used to read file_handle twice. Once to get the amount of extra bytes, and once to fetch the entire structure. This may be problematic since we do size verifications only after the first read, so if the number of extra bytes changes in userspace between the first and second calls, we'll have an incoherent view of file_handle. Instead, read the constant size once, and copy that over to the final structure without having to re-read it again. Change-Id: Ib05e5129629e27d5a05953098c5bc470fae40d2a Signed-off-by: Sasha Levin --- fs/fhandle.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index a48e4a139be1..f7c18e97d852 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -198,8 +198,9 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, goto out_err; } /* copy the full handle */ - if (copy_from_user(handle, ufh, - sizeof(struct file_handle) + + *handle = f_handle; + if (copy_from_user(&handle->f_handle, + &ufh->f_handle, f_handle.handle_bytes)) { retval = -EFAULT; goto out_handle; From 98e990569455ce4e1950bc233ebc5074b8054ace Mon Sep 17 00:00:00 2001 From: Jason Hrycay Date: Fri, 26 Jul 2013 00:22:31 -0500 Subject: [PATCH 059/104] msm: memutils: memcpy, memmove, copy_page optimization Preload farther to take advantage of the memory bus, and assume 64-byte cache lines. Unroll some pairs of ldm/stm as well, for unexplainable reasons. Future enhancements should include, - #define for how far to preload, possibly defined separately for memcpy, copy_*_user - Tuning for misaligned buffers - Tuning for memmove - Tuning for small buffers - Understanding mechanism behind ldm/stm unroll causing some gains in copy_to_user BASELINE (msm8960pro): ====================================================================== memcpy 1000MB at 5MB : took 808850 usec, bandwidth 1236.236 MB/s copy_to_user 1000MB at 5MB : took 810071 usec, bandwidth 1234.234 MB/s copy_from_user 1000MB at 5M: took 942926 usec, bandwidth 1060.060 MB/s memmove 1000GB at 5MB : took 848588 usec, bandwidth 1178.178 MB/s copy_to_user 1000GB at 4kB : took 847916 usec, bandwidth 1179.179 MB/s copy_from_user 1000GB at 4k: took 935113 usec, bandwidth 1069.069 MB/s copy_page 1000GB at 4kB : took 779459 usec, bandwidth 1282.282 MB/s THIS PATCH: ====================================================================== memcpy 1000MB at 5MB : took 346223 usec, bandwidth 2888.888 MB/s copy_to_user 1000MB at 5MB : took 348084 usec, bandwidth 2872.872 MB/s copy_from_user 1000MB at 5M: took 348176 usec, bandwidth 2872.872 MB/s memmove 1000GB at 5MB : took 348267 usec, bandwidth 2871.871 MB/s copy_to_user 1000GB at 4kB : took 377018 usec, bandwidth 2652.652 MB/s copy_from_user 1000GB at 4k: took 371829 usec, bandwidth 2689.689 MB/s copy_page 1000GB at 4kB : took 383763 usec, bandwidth 2605.605 MB/s Change-Id: I843fe77192d61ce35a81e75d76fc09f0a472e98a Signed-off-by: Chris Fries --- arch/arm/lib/Makefile | 8 +- arch/arm/mach-msm/Kconfig | 9 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/memutils/Makefile | 4 + arch/arm/mach-msm/memutils/copy_from_user.S | 110 ++++++++ arch/arm/mach-msm/memutils/copy_page.c | 6 + arch/arm/mach-msm/memutils/copy_template.S | 271 ++++++++++++++++++++ arch/arm/mach-msm/memutils/copy_to_user.S | 125 +++++++++ arch/arm/mach-msm/memutils/memcpy.S | 68 +++++ arch/arm/mach-msm/memutils/memmove.S | 202 +++++++++++++++ 10 files changed, 802 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-msm/memutils/Makefile create mode 100644 arch/arm/mach-msm/memutils/copy_from_user.S create mode 100644 arch/arm/mach-msm/memutils/copy_page.c create mode 100644 arch/arm/mach-msm/memutils/copy_template.S create mode 100644 arch/arm/mach-msm/memutils/copy_to_user.S create mode 100644 arch/arm/mach-msm/memutils/memcpy.S create mode 100644 arch/arm/mach-msm/memutils/memmove.S diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 8ade75d61eb4..e719328b6dd8 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -7,7 +7,7 @@ lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ delay.o delay-loop.o findbit.o memchr.o memcpy.o \ - memmove.o memset.o memzero.o setbit.o \ + memset.o memzero.o setbit.o \ strncpy_from_user.o strnlen_user.o \ strchr.o strrchr.o \ testchangebit.o testclearbit.o testsetbit.o \ @@ -16,10 +16,13 @@ lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ io-readsb.o io-writesb.o io-readsl.o io-writesl.o \ call_with_stack.o -mmu-y := clear_user.o copy_page.o getuser.o putuser.o +mmu-y := clear_user.o getuser.o putuser.o # the code in uaccess.S is not preemption safe and # probably faster on ARMv3 only +ifneq ($(CONFIG_HAS_MACH_MEMUTILS),y) + mmu-y += copy_page.o + lib-y += memmove.o ifeq ($(CONFIG_PREEMPT),y) mmu-y += copy_from_user.o copy_to_user.o else @@ -29,6 +32,7 @@ else mmu-y += uaccess.o endif endif +endif # using lib_ here won't override already available weak symbols obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 461c86745282..2716d68aba02 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -179,6 +179,7 @@ config ARCH_MSM8960 select MSM_USE_USER_ACCESSIBLE_TIMERS select MSM_CPU_PWRCTL select MSM_LPM_TEST + select HAS_MACH_MEMUTILS config ARCH_MSM8930 bool "MSM8930" @@ -530,6 +531,7 @@ config ARCH_MSM8226 select MSM_RPM_STATS_LOG select ARCH_WANT_KMAP_ATOMIC_FLUSH select ENABLE_VMALLOC_SAVINGS + select HAS_MACH_MEMUTILS config ARCH_MSMSAMARIUM bool "MSMSAMARIUM" @@ -3055,4 +3057,11 @@ config KRAIT_REGULATOR line from the PMIC. This supply line is powered by multiple regulators running in ganged mode inside the PMIC. Enable this option to support such configurations. + +config HAS_MACH_MEMUTILS + default n + bool "Motorola MSM memcpy enhancements" + help + Faster memcpy performance for Krait + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index f74606631bf9..30a98471758b 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -421,5 +421,6 @@ obj-$(CONFIG_MSM_DEVFREQ_CPUBW) += devfreq_cpubw.o obj-$(CONFIG_WALL_CLK) += wallclk.o obj-$(CONFIG_WALL_CLK_SYSFS) += wallclk_sysfs.o +obj-$(CONFIG_HAS_MACH_MEMUTILS) += memutils/ obj-$(CONFIG_ARCH_RANDOM) += early_random.o obj-$(CONFIG_PERFMAP) += perfmap.o diff --git a/arch/arm/mach-msm/memutils/Makefile b/arch/arm/mach-msm/memutils/Makefile new file mode 100644 index 000000000000..7f3b6ae02267 --- /dev/null +++ b/arch/arm/mach-msm/memutils/Makefile @@ -0,0 +1,4 @@ + +mach-mem-y := memcpy.o copy_from_user.o copy_to_user.o copy_page.o memmove.o + +obj-$(CONFIG_HAS_MACH_MEMUTILS) += $(mach-mem-y) diff --git a/arch/arm/mach-msm/memutils/copy_from_user.S b/arch/arm/mach-msm/memutils/copy_from_user.S new file mode 100644 index 000000000000..5059a63996ee --- /dev/null +++ b/arch/arm/mach-msm/memutils/copy_from_user.S @@ -0,0 +1,110 @@ +/* + * linux/arch/arm/lib/copy_from_user.S + * + * Author: Nicolas Pitre + * Created: Sep 29, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +/* + * Prototype: + * + * size_t __copy_from_user(void *to, const void *from, size_t n) + * + * Purpose: + * + * copy a block to kernel memory from user memory + * + * Params: + * + * to = kernel memory + * from = user memory + * n = number of bytes to copy + * + * Return value: + * + * Number of bytes NOT copied. + */ + +#ifndef CONFIG_THUMB2_KERNEL +#define LDR1W_SHIFT 0 +#else +#define LDR1W_SHIFT 1 +#endif +#define STR1W_SHIFT 0 + + .macro ldr1w ptr reg abort + ldrusr \reg, \ptr, 4, abort=\abort + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldr1w \ptr, \reg1, \abort + ldr1w \ptr, \reg2, \abort + ldr1w \ptr, \reg3, \abort + ldr1w \ptr, \reg4, \abort + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldr4w \ptr, \reg1, \reg2, \reg3, \reg4, \abort + ldr4w \ptr, \reg5, \reg6, \reg7, \reg8, \abort + .endm + + .macro ldr1b ptr reg cond=al abort + ldrusr \reg, \ptr, 1, \cond, abort=\abort + .endm + + .macro str1w ptr reg abort + W(str) \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldstr8w ptrl ptrw reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldr4w \ptrl, \reg1, \reg2, \reg3, \reg4, \abort + ldr4w \ptrl, \reg5, \reg6, \reg7, \reg8, \abort + stmia \ptrw!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + mov r3, #0 + stmdb sp!, {r0, r2, r3, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + add sp, sp, #8 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +ENTRY(__copy_from_user) + +#include "copy_template.S" + +ENDPROC(__copy_from_user) + + .pushsection .fixup,"ax" + .align 0 + copy_abort_preamble + ldmfd sp!, {r1, r2} + sub r3, r0, r1 + rsb r1, r3, r2 + str r1, [sp] + bl __memzero + ldr r0, [sp], #4 + copy_abort_end + .popsection + diff --git a/arch/arm/mach-msm/memutils/copy_page.c b/arch/arm/mach-msm/memutils/copy_page.c new file mode 100644 index 000000000000..5e276c4b9ed1 --- /dev/null +++ b/arch/arm/mach-msm/memutils/copy_page.c @@ -0,0 +1,6 @@ +#include +#include +void copy_page(void *to, const void *from) +{ + memcpy(to, from, PAGE_SIZE); +} diff --git a/arch/arm/mach-msm/memutils/copy_template.S b/arch/arm/mach-msm/memutils/copy_template.S new file mode 100644 index 000000000000..3a87dd21b151 --- /dev/null +++ b/arch/arm/mach-msm/memutils/copy_template.S @@ -0,0 +1,271 @@ +/* + * linux/arch/arm/lib/copy_template.s + * + * Code template for optimized memory copy functions + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Theory of operation + * ------------------- + * + * This file provides the core code for a forward memory copy used in + * the implementation of memcopy(), copy_to_user() and copy_from_user(). + * + * The including file must define the following accessor macros + * according to the need of the given function: + * + * ldr1w ptr reg abort + * + * This loads one word from 'ptr', stores it in 'reg' and increments + * 'ptr' to the next word. The 'abort' argument is used for fixup tables. + * + * ldr4w ptr reg1 reg2 reg3 reg4 abort + * ldr8w ptr, reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * + * This loads four or eight words starting from 'ptr', stores them + * in provided registers and increments 'ptr' past those words. + * The'abort' argument is used for fixup tables. + * + * ldr1b ptr reg cond abort + * + * Similar to ldr1w, but it loads a byte and increments 'ptr' one byte. + * It also must apply the condition code if provided, otherwise the + * "al" condition is assumed by default. + * + * str1w ptr reg abort + * str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + * str1b ptr reg cond abort + * + * Same as their ldr* counterparts, but data is stored to 'ptr' location + * rather than being loaded. + * + * enter reg1 reg2 + * + * Preserve the provided registers on the stack plus any additional + * data as needed by the implementation including this code. Called + * upon code entry. + * + * exit reg1 reg2 + * + * Restore registers with the values previously saved with the + * 'preserv' macro. Called upon code termination. + * + * LDR1W_SHIFT + * STR1W_SHIFT + * + * Correction to be applied to the "ip" register when branching into + * the ldr1w or str1w instructions (some of these macros may expand to + * than one 32bit instruction in Thumb-2) + */ + + + enter r4, lr + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r0, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #448 ) + /* R2 is now -480 offset from the size passed in*/ + PLD( pld [r1, #64] ) + PLD( pld [r1, #128] ) + PLD( pld [r1, #192] ) + PLD( blt 4f ) + PLD( pld [r1, #256] ) + PLD( pld [r1, #320] ) + PLD( pld [r1, #384] ) + PLD( pld [r1, #448] ) +3: PLD( pld [r1, #512] ) +4: PLD( ldstr8w r1, r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f ) + subs r2, r2, #32 + bge 3b + PLD( cmn r2, #448 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 +#if LDR1W_SHIFT > 0 + lsl ip, ip, #LDR1W_SHIFT +#endif + addne pc, pc, ip @ C is always clear here + b 7f +6: + .rept (1 << LDR1W_SHIFT) + W(nop) + .endr + ldr1w r1, r3, abort=20f + ldr1w r1, r4, abort=20f + ldr1w r1, r5, abort=20f + ldr1w r1, r6, abort=20f + ldr1w r1, r7, abort=20f + ldr1w r1, r8, abort=20f + ldr1w r1, lr, abort=20f + +#if LDR1W_SHIFT < STR1W_SHIFT + lsl ip, ip, #STR1W_SHIFT - LDR1W_SHIFT +#elif LDR1W_SHIFT > STR1W_SHIFT + lsr ip, ip, #LDR1W_SHIFT - STR1W_SHIFT +#endif + add pc, pc, ip + nop + .rept (1 << STR1W_SHIFT) + W(nop) + .endr + str1w r0, r3, abort=20f + str1w r0, r4, abort=20f + str1w r0, r5, abort=20f + str1w r0, r6, abort=20f + str1w r0, r7, abort=20f + str1w r0, r8, abort=20f + str1w r0, lr, abort=20f + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldr1b r1, r3, ne, abort=21f + ldr1b r1, r4, cs, abort=21f + ldr1b r1, ip, cs, abort=21f + str1b r0, r3, ne, abort=21f + str1b r0, r4, cs, abort=21f + str1b r0, ip, cs, abort=21f + + exit r4, pc + +9: rsb ip, ip, #4 + cmp ip, #2 + ldr1b r1, r3, gt, abort=21f + ldr1b r1, r4, ge, abort=21f + ldr1b r1, lr, abort=21f + str1b r0, r3, gt, abort=21f + str1b r0, r4, ge, abort=21f + subs r2, r2, ip + str1b r0, lr, abort=21f + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr1w r1, lr, abort=21f + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r0, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #320 ) + PLD( pld [r1, #64] ) + PLD( pld [r1, #128] ) + PLD( blt 13f ) + PLD( pld [r1, #192] ) + PLD( pld [r1, #256] ) + PLD( pld [r1, #320] ) +12: PLD( pld [r1, #384] ) +13: ldr4w r1, r4, r5, r6, r7, abort=19f + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldr4w r1, r8, r9, ip, lr, abort=19f + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f + bge 12b + PLD( cmn r2, #320 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr1w r1, lr, abort=21f + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str1w r0, r3, abort=21f + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + + +/* + * Abort preamble and completion macros. + * If a fixup handler is required then those macros must surround it. + * It is assumed that the fixup code will handle the private part of + * the exit macro. + */ + + .macro copy_abort_preamble +19: ldmfd sp!, {r5 - r9} + b 21f +20: ldmfd sp!, {r5 - r8} +21: + .endm + + .macro copy_abort_end + ldmfd sp!, {r4, pc} + .endm + diff --git a/arch/arm/mach-msm/memutils/copy_to_user.S b/arch/arm/mach-msm/memutils/copy_to_user.S new file mode 100644 index 000000000000..ca54c28a7053 --- /dev/null +++ b/arch/arm/mach-msm/memutils/copy_to_user.S @@ -0,0 +1,125 @@ +/* + * linux/arch/arm/lib/copy_to_user.S + * + * Author: Nicolas Pitre + * Created: Sep 29, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +/* + * Prototype: + * + * size_t __copy_to_user(void *to, const void *from, size_t n) + * + * Purpose: + * + * copy a block to user memory from kernel memory + * + * Params: + * + * to = user memory + * from = kernel memory + * n = number of bytes to copy + * + * Return value: + * + * Number of bytes NOT copied. + */ + +#define LDR1W_SHIFT 0 +#ifndef CONFIG_THUMB2_KERNEL +#define STR1W_SHIFT 0 +#else +#define STR1W_SHIFT 1 +#endif + + .macro ldr1w ptr reg abort + W(ldr) \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + strusr \reg, \ptr, 4, abort=\abort + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + str1w \ptr, \reg1, \abort + str1w \ptr, \reg2, \abort + str1w \ptr, \reg3, \abort + str1w \ptr, \reg4, \abort + str1w \ptr, \reg5, \abort + str1w \ptr, \reg6, \abort + str1w \ptr, \reg7, \abort + str1w \ptr, \reg8, \abort + .endm + + .macro ldstr8w ptrl ptrw reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldr1w \ptrl, \reg1, \abort + str1w \ptrw, \reg1, \abort + ldr1w \ptrl, \reg2, \abort + str1w \ptrw, \reg2, \abort + ldr1w \ptrl, \reg3, \abort + str1w \ptrw, \reg3, \abort + ldr1w \ptrl, \reg4, \abort + str1w \ptrw, \reg4, \abort + ldr1w \ptrl, \reg5, \abort + str1w \ptrw, \reg5, \abort + ldr1w \ptrl, \reg6, \abort + str1w \ptrw, \reg6, \abort + ldr1w \ptrl, \reg7, \abort + str1w \ptrw, \reg7, \abort + ldr1w \ptrl, \reg8, \abort + str1w \ptrw, \reg8, \abort + .endm + + .macro str1b ptr reg cond=al abort + strusr \reg, \ptr, 1, \cond, abort=\abort + .endm + + .macro enter reg1 reg2 + mov r3, #0 + stmdb sp!, {r0, r2, r3, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + add sp, sp, #8 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +ENTRY(__copy_to_user_std) +WEAK(__copy_to_user) + +#include "copy_template.S" + +ENDPROC(__copy_to_user) +ENDPROC(__copy_to_user_std) + + .pushsection .fixup,"ax" + .align 0 + copy_abort_preamble + ldmfd sp!, {r1, r2, r3} + sub r0, r0, r1 + rsb r0, r0, r2 + copy_abort_end + .popsection + diff --git a/arch/arm/mach-msm/memutils/memcpy.S b/arch/arm/mach-msm/memutils/memcpy.S new file mode 100644 index 000000000000..a35b9bf16b2e --- /dev/null +++ b/arch/arm/mach-msm/memutils/memcpy.S @@ -0,0 +1,68 @@ +/* + * linux/arch/arm/lib/memcpy.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#define LDR1W_SHIFT 0 +#define STR1W_SHIFT 0 + + .macro ldr1w ptr reg abort + W(ldr) \reg, [\ptr], #4 + .endm + + .macro ldr4w ptr reg1 reg2 reg3 reg4 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4} + .endm + + .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldr1b ptr reg cond=al abort + ldr\cond\()b \reg, [\ptr], #1 + .endm + + .macro str1w ptr reg abort + W(str) \reg, [\ptr], #4 + .endm + + .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro ldstr8w ptrl ptrw reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort + ldmia \ptrl!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + stmia \ptrw!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + .endm + + .macro str1b ptr reg cond=al abort + str\cond\()b \reg, [\ptr], #1 + .endm + + .macro enter reg1 reg2 + stmdb sp!, {r0, \reg1, \reg2} + .endm + + .macro exit reg1 reg2 + ldmfd sp!, {r0, \reg1, \reg2} + .endm + + .text + +/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */ + +ENTRY(memcpy) + +#include "copy_template.S" + +ENDPROC(memcpy) diff --git a/arch/arm/mach-msm/memutils/memmove.S b/arch/arm/mach-msm/memutils/memmove.S new file mode 100644 index 000000000000..9eb65512d665 --- /dev/null +++ b/arch/arm/mach-msm/memutils/memmove.S @@ -0,0 +1,202 @@ +/* + * linux/arch/arm/lib/memmove.S + * + * Author: Nicolas Pitre + * Created: Sep 28, 2005 + * Copyright: (C) MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + + .text + +/* + * Prototype: void *memmove(void *dest, const void *src, size_t n); + * + * Note: + * + * If the memory regions don't overlap, we simply branch to memcpy which is + * normally a bit faster. Otherwise the copy is done going downwards. This + * is a transposition of the code from copy_template.S but with the copy + * occurring in the opposite direction. + */ + +ENTRY(memmove) + + subs ip, r0, r1 + cmphi r2, ip + bls memcpy + + stmfd sp!, {r0, r4, lr} + add r1, r1, r2 + add r0, r0, r2 + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #-4] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r0, #31 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, ip ) @ C is set here + CALGN( rsb ip, ip, #32 ) + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #-4] ) +2: PLD( subs r2, r2, #320 ) + PLD( pld [r1, #-68] ) + PLD( pld [r1, #-132] ) + PLD( blt 4f ) + PLD( pld [r1, #-196] ) + PLD( pld [r1, #-260] ) + PLD( pld [r1, #-324] ) + +3: PLD( pld [r1, #-388] ) +4: ldmdb r1!, {r3, r4, r5, r6, r7, r8, ip, lr} + subs r2, r2, #32 + stmdb r0!, {r3, r4, r5, r6, r7, r8, ip, lr} + bge 3b + PLD( cmn r2, #320 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: W(nop) + W(ldr) r3, [r1, #-4]! + W(ldr) r4, [r1, #-4]! + W(ldr) r5, [r1, #-4]! + W(ldr) r6, [r1, #-4]! + W(ldr) r7, [r1, #-4]! + W(ldr) r8, [r1, #-4]! + W(ldr) lr, [r1, #-4]! + + add pc, pc, ip + nop + W(nop) + W(str) r3, [r0, #-4]! + W(str) r4, [r0, #-4]! + W(str) r5, [r0, #-4]! + W(str) r6, [r0, #-4]! + W(str) r7, [r0, #-4]! + W(str) r8, [r0, #-4]! + W(str) lr, [r0, #-4]! + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldrneb r3, [r1, #-1]! + ldrcsb r4, [r1, #-1]! + ldrcsb ip, [r1, #-1] + strneb r3, [r0, #-1]! + strcsb r4, [r0, #-1]! + strcsb ip, [r0, #-1] + ldmfd sp!, {r0, r4, pc} + +9: cmp ip, #2 + ldrgtb r3, [r1, #-1]! + ldrgeb r4, [r1, #-1]! + ldrb lr, [r1, #-1]! + strgtb r3, [r0, #-1]! + strgeb r4, [r0, #-1]! + subs r2, r2, ip + strb lr, [r0, #-1]! + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr r3, [r1, #0] + beq 17f + blt 18f + + + .macro backward_copy_shift push pull + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r0, #31 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #-4] ) + PLD( subs r2, r2, #320 ) + PLD( pld [r1, #-68] ) + PLD( pld [r1, #-132] ) + PLD( blt 13f ) + PLD( pld [r1, #-196] ) + PLD( pld [r1, #-260] ) + PLD( pld [r1, #-324] ) +12: PLD( pld [r1, #-388] ) +13: ldmdb r1!, {r7, r8, r9, ip} + mov lr, r3, push #\push + subs r2, r2, #32 + ldmdb r1!, {r3, r4, r5, r6} + orr lr, lr, ip, pull #\pull + mov ip, ip, push #\push + orr ip, ip, r9, pull #\pull + mov r9, r9, push #\push + orr r9, r9, r8, pull #\pull + mov r8, r8, push #\push + orr r8, r8, r7, pull #\pull + mov r7, r7, push #\push + orr r7, r7, r6, pull #\pull + mov r6, r6, push #\push + orr r6, r6, r5, pull #\pull + mov r5, r5, push #\push + orr r5, r5, r4, pull #\pull + mov r4, r4, push #\push + orr r4, r4, r3, pull #\pull + stmdb r0!, {r4 - r9, ip, lr} + bge 12b + PLD( cmn r2, #320 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov lr, r3, push #\push + ldr r3, [r1, #-4]! + subs ip, ip, #4 + orr lr, lr, r3, pull #\pull + str lr, [r0, #-4]! + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: add r1, r1, #(\pull / 8) + b 8b + + .endm + + + backward_copy_shift push=8 pull=24 + +17: backward_copy_shift push=16 pull=16 + +18: backward_copy_shift push=24 pull=8 + +ENDPROC(memmove) From d0f504b5b3a1275610b56e2502cecd8cff4cd46f Mon Sep 17 00:00:00 2001 From: Hong-Mei Li Date: Fri, 5 Jul 2013 02:33:53 +0800 Subject: [PATCH 060/104] msm: memutils: memcpy, memmove optimization 1. To fit 8x26 64-bytes cache line size, we change preload steps as 64Bytes. 2. According to tune result, change preload distance as 5 cache lines. 3. According to tune result, re-arrange the ld/str order as back-to-back for copy_from_user. The markable improvement: memcpy : 5% copy_to_user : 9% copy_from_user : 13% memmove : 37% Raw data is as below: BASELINE: memcpy 1000MB at 5MB : took 1547098 usec, bandwidth 646.646 MB/s copy_to_user 1000MB at 5MB : took 1704308 usec, bandwidth 586.586 MB/s copy_from_user 1000MB at 5M: took 1777090 usec, bandwidth 562.562 MB/s memmove 1000GB at 5MB : took 1066205 usec, bandwidth 937.937 MB/s copy_to_user 1000GB at 4kB : took 1774866 usec, bandwidth 563.563 MB/s copy_from_user 1000GB at 4k: took 1797654 usec, bandwidth 556.556 MB/s copy_page 1000GB at 4kB : took 1644606 usec, bandwidth 608.608 MB/s memmove 1000GB at 4kB : took 1236227 usec, bandwidth 808.808 MB/s THIS PATCH: memcpy 1000MB at 5MB : took 1475835 usec, bandwidth 677.677 MB/s copy_to_user 1000MB at 5MB : took 1559060 usec, bandwidth 641.641 MB/s copy_from_user 1000MB at 5M: took 1561603 usec, bandwidth 640.640 MB/s memmove 1000GB at 5MB : took 861664 usec, bandwidth 1160.160 MB/s copy_to_user 1000GB at 4kB : took 1673501 usec, bandwidth 597.597 MB/s copy_from_user 1000GB at 4k: took 1674006 usec, bandwidth 597.597 MB/s copy_page 1000GB at 4kB : took 1691358 usec, bandwidth 591.591 MB/s memmove 1000GB at 4kB : took 882985 usec, bandwidth 1132.132 MB/s Change-Id: I83bec3b7a9dd9cd88890eaa7ec423363e230a651 Signed-off-by: Hong-Mei Li Reviewed-on: http://gerrit.pcs.mot.com/550383 SLT-Approved: Slta Waiver Tested-by: Jira Key Reviewed-by: Klocwork kwcheck Reviewed-by: Check Patch Reviewed-by: Yi-Wei Zhao Submit-Approved: Jira Key --- arch/arm/mach-msm/memutils/copy_from_user.S | 19 +++++++++++++--- arch/arm/mach-msm/memutils/copy_template.S | 24 ++++++++------------- arch/arm/mach-msm/memutils/memmove.S | 17 ++++++--------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/memutils/copy_from_user.S b/arch/arm/mach-msm/memutils/copy_from_user.S index 5059a63996ee..b6ea5b489303 100644 --- a/arch/arm/mach-msm/memutils/copy_from_user.S +++ b/arch/arm/mach-msm/memutils/copy_from_user.S @@ -69,9 +69,22 @@ .endm .macro ldstr8w ptrl ptrw reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort - ldr4w \ptrl, \reg1, \reg2, \reg3, \reg4, \abort - ldr4w \ptrl, \reg5, \reg6, \reg7, \reg8, \abort - stmia \ptrw!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8} + ldr1w \ptrl, \reg1, \abort + str1w \ptrw, \reg1, \abort + ldr1w \ptrl, \reg2, \abort + str1w \ptrw, \reg2, \abort + ldr1w \ptrl, \reg3, \abort + str1w \ptrw, \reg3, \abort + ldr1w \ptrl, \reg4, \abort + str1w \ptrw, \reg4, \abort + ldr1w \ptrl, \reg5, \abort + str1w \ptrw, \reg5, \abort + ldr1w \ptrl, \reg6, \abort + str1w \ptrw, \reg6, \abort + ldr1w \ptrl, \reg7, \abort + str1w \ptrw, \reg7, \abort + ldr1w \ptrl, \reg8, \abort + str1w \ptrw, \reg8, \abort .endm .macro str1b ptr reg cond=al abort diff --git a/arch/arm/mach-msm/memutils/copy_template.S b/arch/arm/mach-msm/memutils/copy_template.S index 3a87dd21b151..aeab8486b712 100644 --- a/arch/arm/mach-msm/memutils/copy_template.S +++ b/arch/arm/mach-msm/memutils/copy_template.S @@ -90,21 +90,18 @@ CALGN( add pc, r4, ip ) PLD( pld [r1, #0] ) -2: PLD( subs r2, r2, #448 ) +2: PLD( subs r2, r2, #192 ) /* R2 is now -480 offset from the size passed in*/ PLD( pld [r1, #64] ) + PLD( blt 4f ) PLD( pld [r1, #128] ) PLD( pld [r1, #192] ) - PLD( blt 4f ) - PLD( pld [r1, #256] ) - PLD( pld [r1, #320] ) - PLD( pld [r1, #384] ) - PLD( pld [r1, #448] ) -3: PLD( pld [r1, #512] ) + +3: PLD( pld [r1, #256] ) 4: PLD( ldstr8w r1, r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f ) subs r2, r2, #32 bge 3b - PLD( cmn r2, #448 ) + PLD( cmn r2, #192 ) PLD( bge 4b ) 5: ands ip, r2, #28 @@ -192,14 +189,11 @@ 11: stmfd sp!, {r5 - r9} PLD( pld [r1, #0] ) - PLD( subs r2, r2, #320 ) + PLD( subs r2, r2, #128 ) PLD( pld [r1, #64] ) - PLD( pld [r1, #128] ) PLD( blt 13f ) - PLD( pld [r1, #192] ) - PLD( pld [r1, #256] ) - PLD( pld [r1, #320] ) -12: PLD( pld [r1, #384] ) + PLD( pld [r1, #128] ) +12: PLD( pld [r1, #192] ) 13: ldr4w r1, r4, r5, r6, r7, abort=19f mov r3, lr, pull #\pull subs r2, r2, #32 @@ -221,7 +215,7 @@ orr ip, ip, lr, push #\push str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f bge 12b - PLD( cmn r2, #320 ) + PLD( cmn r2, #128 ) PLD( bge 13b ) ldmfd sp!, {r5 - r9} diff --git a/arch/arm/mach-msm/memutils/memmove.S b/arch/arm/mach-msm/memutils/memmove.S index 9eb65512d665..0f6b3b2134ed 100644 --- a/arch/arm/mach-msm/memutils/memmove.S +++ b/arch/arm/mach-msm/memutils/memmove.S @@ -56,20 +56,19 @@ ENTRY(memmove) CALGN( add pc, r4, ip ) PLD( pld [r1, #-4] ) -2: PLD( subs r2, r2, #320 ) +2: PLD( subs r2, r2, #192 ) PLD( pld [r1, #-68] ) PLD( pld [r1, #-132] ) PLD( blt 4f ) PLD( pld [r1, #-196] ) - PLD( pld [r1, #-260] ) - PLD( pld [r1, #-324] ) -3: PLD( pld [r1, #-388] ) +3: PLD( pld [r1, #-260] ) + 4: ldmdb r1!, {r3, r4, r5, r6, r7, r8, ip, lr} subs r2, r2, #32 stmdb r0!, {r3, r4, r5, r6, r7, r8, ip, lr} bge 3b - PLD( cmn r2, #320 ) + PLD( cmn r2, #192 ) PLD( bge 4b ) 5: ands ip, r2, #28 @@ -141,14 +140,12 @@ ENTRY(memmove) 11: stmfd sp!, {r5 - r9} PLD( pld [r1, #-4] ) - PLD( subs r2, r2, #320 ) + PLD( subs r2, r2, #192 ) PLD( pld [r1, #-68] ) PLD( pld [r1, #-132] ) PLD( blt 13f ) PLD( pld [r1, #-196] ) - PLD( pld [r1, #-260] ) - PLD( pld [r1, #-324] ) -12: PLD( pld [r1, #-388] ) +12: PLD( pld [r1, #-260] ) 13: ldmdb r1!, {r7, r8, r9, ip} mov lr, r3, push #\push subs r2, r2, #32 @@ -170,7 +167,7 @@ ENTRY(memmove) orr r4, r4, r3, pull #\pull stmdb r0!, {r4 - r9, ip, lr} bge 12b - PLD( cmn r2, #320 ) + PLD( cmn r2, #192 ) PLD( bge 13b ) ldmfd sp!, {r5 - r9} From 5769ddceb2da3c2f2c702eba647489d04570b696 Mon Sep 17 00:00:00 2001 From: vm03 Date: Mon, 30 Mar 2015 16:04:21 +0300 Subject: [PATCH 061/104] enable optimized memutils --- arch/arm/configs/w3ds_global_com_defconfig | 1 + arch/arm/configs/w5_global_com_defconfig | 1 + arch/arm/configs/w5ds_global_com_defconfig | 1 + arch/arm/configs/w5n_global_com_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index acaf8bb08153..3d3c98e56ad7 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -79,6 +79,7 @@ CONFIG_MSM_OCMEM_NONSECURE=y CONFIG_MSM_OCMEM_POWER_DISABLE=y CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y CONFIG_MSM_BOOT_STATS=y +CONFIG_HAS_MACH_MEMUTILS=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_SMP=y diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig index 8dbc92b75da1..9b550902bd41 100644 --- a/arch/arm/configs/w5_global_com_defconfig +++ b/arch/arm/configs/w5_global_com_defconfig @@ -79,6 +79,7 @@ CONFIG_MSM_OCMEM_NONSECURE=y CONFIG_MSM_OCMEM_POWER_DISABLE=y CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y CONFIG_MSM_BOOT_STATS=y +CONFIG_HAS_MACH_MEMUTILS=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_SMP=y diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index d13e08a20893..8483407bd8be 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -79,6 +79,7 @@ CONFIG_MSM_OCMEM_NONSECURE=y CONFIG_MSM_OCMEM_POWER_DISABLE=y CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y CONFIG_MSM_BOOT_STATS=y +CONFIG_HAS_MACH_MEMUTILS=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_SMP=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 392c518e7ffa..8083d99ac4c0 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -79,6 +79,7 @@ CONFIG_MSM_OCMEM_NONSECURE=y CONFIG_MSM_OCMEM_POWER_DISABLE=y CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y CONFIG_MSM_BOOT_STATS=y +CONFIG_HAS_MACH_MEMUTILS=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_SMP=y From 76eec7089ab505a250750cdc8a3b8864ebb95bb4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 18 Feb 2015 18:26:11 +0200 Subject: [PATCH 062/104] netfilter: conntrack: disable generic tracking for known protocols Given following iptables ruleset: -P FORWARD DROP -A FORWARD -m sctp --dport 9 -j ACCEPT -A FORWARD -p tcp --dport 80 -j ACCEPT -A FORWARD -p tcp -m conntrack -m state ESTABLISHED,RELATED -j ACCEPT One would assume that this allows SCTP on port 9 and TCP on port 80. Unfortunately, if the SCTP conntrack module is not loaded, this allows *all* SCTP communication, to pass though, i.e. -p sctp -j ACCEPT, which we think is a security issue. This is because on the first SCTP packet on port 9, we create a dummy "generic l4" conntrack entry without any port information (since conntrack doesn't know how to extract this information). All subsequent packets that are unknown will then be in established state since they will fallback to proto_generic and will match the 'generic' entry. Our originally proposed version [1] completely disabled generic protocol tracking, but Jozsef suggests to not track protocols for which a more suitable helper is available, hence we now mitigate the issue for in tree known ct protocol helpers only, so that at least NAT and direction information will still be preserved for others. [1] http://www.spinics.net/lists/netfilter-devel/msg33430.html Joint work with Daniel Borkmann. Change-Id: I7fff74303d98876efd3e7834555cbf95d0319359 Signed-off-by: Florian Westphal Signed-off-by: Daniel Borkmann Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_generic.c | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 835e24c58f0d..54e3900a49db 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -14,6 +14,30 @@ static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ; +static bool nf_generic_should_process(u8 proto) +{ + switch (proto) { +#ifdef CONFIG_NF_CT_PROTO_SCTP_MODULE + case IPPROTO_SCTP: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_DCCP_MODULE + case IPPROTO_DCCP: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_GRE_MODULE + case IPPROTO_GRE: + return false; +#endif +#ifdef CONFIG_NF_CT_PROTO_UDPLITE_MODULE + case IPPROTO_UDPLITE: + return false; +#endif + default: + return true; + } +} + static bool generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, struct nf_conntrack_tuple *tuple) @@ -62,7 +86,7 @@ static int generic_packet(struct nf_conn *ct, static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, unsigned int *timeouts) { - return true; + return nf_generic_should_process(nf_ct_protonum(ct)); } #if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT) From e80aaf06995fa8935a695735b61c5ae49139ebf9 Mon Sep 17 00:00:00 2001 From: Michael Halcrow Date: Wed, 18 Feb 2015 18:28:45 +0200 Subject: [PATCH 063/104] eCryptfs: Remove buggy and unnecessary write in file name decode routine Dmitry Chernenkov used KASAN to discover that eCryptfs writes past the end of the allocated buffer during encrypted filename decoding. This fix corrects the issue by getting rid of the unnecessary 0 write when the current bit offset is 2. Change-Id: I2e139f816b9ce0ad6d207c6f454d6f25061383ee Signed-off-by: Michael Halcrow Reported-by: Dmitry Chernenkov Suggested-by: Kees Cook Cc: stable@vger.kernel.org # v2.6.29+: 51ca58d eCryptfs: Filename Encryption: Encoding and encryption functions Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index ea9931281557..3d0633891f28 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -2044,7 +2044,6 @@ ecryptfs_decode_from_filename(unsigned char *dst, size_t *dst_size, break; case 2: dst[dst_byte_offset++] |= (src_byte); - dst[dst_byte_offset] = 0; current_bit_offset = 0; break; } From 3a8d72c60aee6cf86ba5a5b8af3ba3ee002f538f Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Fri, 23 Jan 2015 12:01:26 +0100 Subject: [PATCH 064/104] ipv4: try to cache dst_entries which would cause a redirect Not caching dst_entries which cause redirects could be exploited by hosts on the same subnet, causing a severe DoS attack. This effect aggravated since commit f88649721268999 ("ipv4: fix dst race in sk_dst_get()"). Lookups causing redirects will be allocated with DST_NOCACHE set which will force dst_release to free them via RCU. Unfortunately waiting for RCU grace period just takes too long, we can end up with >1M dst_entries waiting to be released and the system will run OOM. rcuos threads cannot catch up under high softirq load. Attaching the flag to emit a redirect later on to the specific skb allows us to cache those dst_entries thus reducing the pressure on allocation and deallocation. This issue was discovered by Marcelo Leitner. Cc: Julian Anastasov Signed-off-by: Marcelo Leitner Signed-off-by: Florian Westphal Signed-off-by: Hannes Frederic Sowa Signed-off-by: Julian Anastasov Signed-off-by: David S. Miller Conflicts: include/net/ip.h net/ipv4/route.c Change-Id: I0a879810b301e122a50b0b206c01f75f32682ca4 --- include/net/ip.h | 11 ++++++----- net/ipv4/ip_forward.c | 3 ++- net/ipv4/route.c | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index e36ffa109b5f..76f0c09cd01c 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -37,11 +37,12 @@ struct inet_skb_parm { struct ip_options opt; /* Compiled IP options */ unsigned char flags; -#define IPSKB_FORWARDED 1 -#define IPSKB_XFRM_TUNNEL_SIZE 2 -#define IPSKB_XFRM_TRANSFORMED 4 -#define IPSKB_FRAG_COMPLETE 8 -#define IPSKB_REROUTED 16 +#define IPSKB_FORWARDED BIT(0) +#define IPSKB_XFRM_TUNNEL_SIZE BIT(1) +#define IPSKB_XFRM_TRANSFORMED BIT(2) +#define IPSKB_FRAG_COMPLETE BIT(3) +#define IPSKB_REROUTED BIT(4) +#define IPSKB_DOREDIRECT BIT(5) }; static inline unsigned int ip_hdrlen(const struct sk_buff *skb) diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 29a07b6c7168..0ee2d50c634d 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -107,7 +107,8 @@ int ip_forward(struct sk_buff *skb) * We now generate an ICMP HOST REDIRECT giving the route * we calculated. */ - if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb)) + if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr && + !skb_sec_path(skb)) ip_rt_send_redirect(skb); skb->priority = rt_tos2priority(iph->tos); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 3ab9d27f5ab3..2de2ee7f1657 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2157,9 +2157,10 @@ static int __mkroute_input(struct sk_buff *skb, flags |= RTCF_DIRECTSRC; if (out_dev == in_dev && err && + skb->protocol == htons(ETH_P_IP) && (IN_DEV_SHARED_MEDIA(out_dev) || inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) - flags |= RTCF_DOREDIRECT; + IPCB(skb)->flags |= IPSKB_DOREDIRECT; if (skb->protocol != htons(ETH_P_IP)) { /* Not IP (i.e. ARP). Do not create route, if it is @@ -2988,6 +2989,8 @@ static int rt_fill_info(struct net *net, r->rtm_flags = (rt->rt_flags & ~0xFFFF) | RTM_F_CLONED; if (rt->rt_flags & RTCF_NOTIFY) r->rtm_flags |= RTM_F_NOTIFY; + if (IPCB(skb)->flags & IPSKB_DOREDIRECT) + r->rtm_flags |= RTCF_DOREDIRECT; NLA_PUT_BE32(skb, RTA_DST, rt->rt_dst); From a5138ea98b8d49c5e7914b284fdb9eec152895fe Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 3 May 2014 14:04:59 +0200 Subject: [PATCH 065/104] n_tty: Fix n_tty_write crash when echoing in raw mode commit 4291086b1f081b869c6d79e5b7441633dc3ace00 upstream. The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows. If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space; so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM B's memcpy is past the tty_buffer due to the previous A's tb->used increment. Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine. Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is present in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. js: add more info to the commit log js: switch to bool js: lock unconditionally js: lock only the tty->ops->write call References: CVE-2014-0196 Reported-and-tested-by: Jiri Slaby Signed-off-by: Peter Hurley Signed-off-by: Jiri Slaby Cc: Linus Torvalds Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman [bwh: Backported to 3.2: output_lock is a member of struct tty_struct] Signed-off-by: Ben Hutchings --- drivers/tty/n_tty.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 8eb55731dca4..91cae305b888 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1999,7 +1999,9 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, tty->ops->flush_chars(tty); } else { while (nr > 0) { + mutex_lock(&tty->output_lock); c = tty->ops->write(tty, b, nr); + mutex_unlock(&tty->output_lock); if (c < 0) { retval = c; goto break_out; From b76180a9d4384dc116bb363e4561669d2619da73 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Jun 2014 12:27:06 +0000 Subject: [PATCH 066/104] futex-prevent-requeue-pi-on-same-futex.patch futex: Forbid uaddr == uaddr2 in futex_requeue(..., requeue_pi=1) commit e9c243a5a6de0be8e584c604d353412584b592f8 upstream. If uaddr == uaddr2, then we have broken the rule of only requeueing from a non-pi futex to a pi futex with this call. If we attempt this, then dangling pointers may be left for rt_waiter resulting in an exploitable condition. This change brings futex_requeue() in line with futex_wait_requeue_pi() which performs the same check as per commit 6f7b0a2a5c0f ("futex: Forbid uaddr == uaddr2 in futex_wait_requeue_pi()") [ tglx: Compare the resulting keys as well, as uaddrs might be different depending on the mapping ] Fixes CVE-2014-3153. Reported-by: Pinkie Pie Signed-off-by: Will Drewry Signed-off-by: Kees Cook Signed-off-by: Thomas Gleixner Reviewed-by: Darren Hart Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/futex.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/kernel/futex.c b/kernel/futex.c index e2b0fb9a0b3b..9536918cac29 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1255,6 +1255,13 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags, u32 curval2; if (requeue_pi) { + /* + * Requeue PI only works on two distinct uaddrs. This + * check is only valid for private futexes. See below. + */ + if (uaddr1 == uaddr2) + return -EINVAL; + /* * requeue_pi requires a pi_state, try to allocate it now * without any locks in case it fails. @@ -1293,6 +1300,15 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags, if (unlikely(ret != 0)) goto out_put_key1; + /* + * The check above which compares uaddrs is not sufficient for + * shared futexes. We need to compare the keys: + */ + if (requeue_pi && match_futex(&key1, &key2)) { + ret = -EINVAL; + goto out_put_keys; + } + hb1 = hash_futex(&key1); hb2 = hash_futex(&key2); @@ -2308,6 +2324,15 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, if (ret) goto out_key2; + /* + * The check above which compares uaddrs is not sufficient for + * shared futexes. We need to compare the keys: + */ + if (match_futex(&q.key, &key2)) { + ret = -EINVAL; + goto out_put_keys; + } + /* Queue the futex_q, drop the hb lock, wait for wakeup. */ futex_wait_queue_me(hb, &q, to); From 1c30d13bb9e7158edc7beed154c60d0a1dd1dd88 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 14 Jul 2014 17:02:31 -0700 Subject: [PATCH 067/104] net/l2tp: don't fall back on UDP [get|set]sockopt The l2tp [get|set]sockopt() code has fallen back to the UDP functions for socket option levels != SOL_PPPOL2TP since day one, but that has never actually worked, since the l2tp socket isn't an inet socket. As David Miller points out: "If we wanted this to work, it'd have to look up the tunnel and then use tunnel->sk, but I wonder how useful that would be" Since this can never have worked so nobody could possibly have depended on that functionality, just remove the broken code and return -EINVAL. Reported-by: Sasha Levin Acked-by: James Chapman Acked-by: David Miller Cc: Phil Turnbull Cc: Vegard Nossum Cc: Willy Tarreau Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds Change-Id: Ia6c09487e92d1ee4e8d1d22a6273034b22272a2f --- net/l2tp/l2tp_ppp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 1addd9f3f40a..efb0138948b1 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1348,7 +1348,7 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname, int err; if (level != SOL_PPPOL2TP) - return udp_prot.setsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (optlen < sizeof(int)) return -EINVAL; @@ -1474,7 +1474,7 @@ static int pppol2tp_getsockopt(struct socket *sock, int level, struct pppol2tp_session *ps; if (level != SOL_PPPOL2TP) - return udp_prot.getsockopt(sk, level, optname, optval, optlen); + return -EINVAL; if (get_user(len, (int __user *) optlen)) return -EFAULT; From da49c4adcb85bd7ebe38bd5b34cf4948daeb7367 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 1 Apr 2015 12:58:12 +0300 Subject: [PATCH 068/104] enable LCD_KCAL in w5n defconfig --- arch/arm/configs/w5n_global_com_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 8083d99ac4c0..3d8cb35bd29d 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -422,6 +422,7 @@ CONFIG_FB_MSM=y CONFIG_FB_MSM_MDSS=y CONFIG_FB_MSM_MDSS_WRITEBACK=y CONFIG_FB_MSM_MDSS_MDP3=y +CONFIG_LCD_KCAL=y CONFIG_BACKLIGHT_LCD_SUPPORT=y # CONFIG_LCD_CLASS_DEVICE is not set CONFIG_BACKLIGHT_CLASS_DEVICE=y From 82ab26bccb7672ad587c15c5bfbc0343538fce56 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Fri, 28 Mar 2014 13:54:32 +0400 Subject: [PATCH 069/104] netfilter: nf_conntrack: reserve two bytes for nf_ct_ext->len "len" contains sizeof(nf_ct_ext) and size of extensions. In a worst case it can contain all extensions. Bellow you can find sizes for all types of extensions. Their sum is definitely bigger than 256. nf_ct_ext_types[0]->len = 24 nf_ct_ext_types[1]->len = 32 nf_ct_ext_types[2]->len = 24 nf_ct_ext_types[3]->len = 32 nf_ct_ext_types[4]->len = 152 nf_ct_ext_types[5]->len = 2 nf_ct_ext_types[6]->len = 16 nf_ct_ext_types[7]->len = 8 I have seen "len" up to 280 and my host has crashes w/o this patch. The right way to fix this problem is reducing the size of the ecache extension (4) and Florian is going to do this, but these changes will be quite large to be appropriate for a stable tree. Change-Id: If9efaf2b103cf304bbfa583e354cfad3faa77ac2 Fixes: 5b423f6a40a0 (netfilter: nf_conntrack: fix racy timer handling with reliable) Cc: Pablo Neira Ayuso Cc: Patrick McHardy Cc: Jozsef Kadlecsik Cc: "David S. Miller" Signed-off-by: Andrey Vagin Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 96755c3798a5..0066409f86c7 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -37,8 +37,8 @@ enum nf_ct_ext_id { /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { struct rcu_head rcu; - u8 offset[NF_CT_EXT_NUM]; - u8 len; + u16 offset[NF_CT_EXT_NUM]; + u16 len; char data[0]; }; From 5e24efa9d33a0904a41a09d921e6799798768f1e Mon Sep 17 00:00:00 2001 From: "D.S. Ljungmark" Date: Wed, 25 Mar 2015 09:28:15 +0100 Subject: [PATCH 070/104] ipv6: Don't reduce hop limit for an interface A local route may have a lower hop_limit set than global routes do. RFC 3756, Section 4.2.7, "Parameter Spoofing" > 1. The attacker includes a Current Hop Limit of one or another small > number which the attacker knows will cause legitimate packets to > be dropped before they reach their destination. > As an example, one possible approach to mitigate this threat is to > ignore very small hop limits. The nodes could implement a > configurable minimum hop limit, and ignore attempts to set it below > said limit. Change-Id: I51ee1778e3d2d5fa1aefbdf1ad8869e4e8dc28b2 Signed-off-by: D.S. Ljungmark Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 176b469322ac..7ac6811ea797 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1266,7 +1266,14 @@ static void ndisc_router_discovery(struct sk_buff *skb) if (rt) rt6_set_expires(rt, jiffies + (HZ * lifetime)); if (ra_msg->icmph.icmp6_hop_limit) { - in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; + /* Only set hop_limit on the interface if it is higher than + * the current hop_limit. + */ + if (in6_dev->cnf.hop_limit < ra_msg->icmph.icmp6_hop_limit) { + in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; + } else { + ND_PRINTK2(KERN_WARNING "RA: Got route advertisement with lower hop_limit than current\n"); + } if (rt) dst_metric_set(&rt->dst, RTAX_HOPLIMIT, ra_msg->icmph.icmp6_hop_limit); From 888071c77337a2dfe40f38e530a69324d332ca23 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 23 Jan 2015 20:47:00 -0500 Subject: [PATCH 071/104] net: llc: use correct size for sysctl timeout entries The timeout entries are sizeof(int) rather than sizeof(long), which means that when they were getting read we'd also leak kernel memory to userspace along with the timeout values. Change-Id: I328d1186720a6f70f555eeeb62c83ee69814868d Signed-off-by: Sasha Levin Signed-off-by: David S. Miller --- net/llc/sysctl_net_llc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index e2ebe3586263..be078ec43d7f 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c @@ -17,28 +17,28 @@ static struct ctl_table llc2_timeout_table[] = { { .procname = "ack", .data = &sysctl_llc2_ack_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_ack_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "busy", .data = &sysctl_llc2_busy_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_busy_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "p", .data = &sysctl_llc2_p_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_p_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, { .procname = "rej", .data = &sysctl_llc2_rej_timeout, - .maxlen = sizeof(long), + .maxlen = sizeof(sysctl_llc2_rej_timeout), .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, From bb48f3cef2494c05efbf05db0729829d773f35b1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 1 May 2015 22:02:47 -0400 Subject: [PATCH 072/104] ipv4: Missing sk_nulls_node_init() in ping_unhash(). If we don't do that, then the poison value is left in the ->pprev backlink. This can cause crashes if we do a disconnect, followed by a connect(). Tested-by: Linus Torvalds Reported-by: Wen Xu Signed-off-by: David S. Miller Change-Id: If41b5e0fa2c8c028cb3807dc5b723ebd4552221f --- net/ipv4/ping.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 7859ce552dfc..d8c8b01b1428 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -138,6 +138,7 @@ static void ping_v4_unhash(struct sock *sk) if (sk_hashed(sk)) { write_lock_bh(&ping_table.lock); hlist_nulls_del(&sk->sk_nulls_node); + sk_nulls_node_init(&sk->sk_nulls_node); sock_put(sk); isk->inet_num = 0; isk->inet_sport = 0; From c193b440424c0414dc48d41e33c98b0871207dbc Mon Sep 17 00:00:00 2001 From: vm03 Date: Tue, 5 May 2015 17:17:04 +0300 Subject: [PATCH 073/104] Enable LZ4 compression for ZRAM Change-Id: I33afd4f1596e0e4174067f15a7f27a9e792e02f3 --- arch/arm/configs/w3ds_global_com_defconfig | 3 +++ arch/arm/configs/w5_global_com_defconfig | 3 +++ arch/arm/configs/w5ds_global_com_defconfig | 3 +++ arch/arm/configs/w5n_global_com_defconfig | 3 +++ 4 files changed, 12 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index 3d3c98e56ad7..1e6f105d1bc5 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -1,5 +1,7 @@ # CONFIG_ARM_PATCH_PHYS_VIRT is not set CONFIG_EXPERIMENTAL=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_AUDIT=y @@ -466,6 +468,7 @@ CONFIG_UIO=y CONFIG_UIO_MSM_SHAREDMEM=y CONFIG_STAGING=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_ZSMALLOC=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm/configs/w5_global_com_defconfig b/arch/arm/configs/w5_global_com_defconfig index 9b550902bd41..6eacf9aa5676 100644 --- a/arch/arm/configs/w5_global_com_defconfig +++ b/arch/arm/configs/w5_global_com_defconfig @@ -1,5 +1,7 @@ # CONFIG_ARM_PATCH_PHYS_VIRT is not set CONFIG_EXPERIMENTAL=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_AUDIT=y @@ -469,6 +471,7 @@ CONFIG_UIO=y CONFIG_UIO_MSM_SHAREDMEM=y CONFIG_STAGING=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_ZSMALLOC=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm/configs/w5ds_global_com_defconfig b/arch/arm/configs/w5ds_global_com_defconfig index 8483407bd8be..6bfa59238c60 100644 --- a/arch/arm/configs/w5ds_global_com_defconfig +++ b/arch/arm/configs/w5ds_global_com_defconfig @@ -1,5 +1,7 @@ # CONFIG_ARM_PATCH_PHYS_VIRT is not set CONFIG_EXPERIMENTAL=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_AUDIT=y @@ -463,6 +465,7 @@ CONFIG_UIO=y CONFIG_UIO_MSM_SHAREDMEM=y CONFIG_STAGING=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_ZSMALLOC=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 3d8cb35bd29d..54328d1d4328 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -1,5 +1,7 @@ # CONFIG_ARM_PATCH_PHYS_VIRT is not set CONFIG_EXPERIMENTAL=y +CONFIG_LZ4_COMPRESS=y +CONFIG_LZ4_DECOMPRESS=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y CONFIG_AUDIT=y @@ -468,6 +470,7 @@ CONFIG_UIO=y CONFIG_UIO_MSM_SHAREDMEM=y CONFIG_STAGING=y CONFIG_ZRAM=y +CONFIG_ZRAM_LZ4_COMPRESS=y CONFIG_ZSMALLOC=y CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y From 0d27514173b899ffd54c5b37cce60c4a2d302535 Mon Sep 17 00:00:00 2001 From: Kyungsik Lee Date: Mon, 8 Jul 2013 16:01:45 -0700 Subject: [PATCH 074/104] decompressor: add LZ4 decompressor module Add support for LZ4 decompression in the Linux Kernel. LZ4 Decompression APIs for kernel are based on LZ4 implementation by Yann Collet. Benchmark Results(PATCH v3) Compiler: Linaro ARM gcc 4.6.2 1. ARMv7, 1.5GHz based board Kernel: linux 3.4 Uncompressed Kernel Size: 14MB Compressed Size Decompression Speed LZO 6.7MB 20.1MB/s, 25.2MB/s(UA) LZ4 7.3MB 29.1MB/s, 45.6MB/s(UA) 2. ARMv7, 1.7GHz based board Kernel: linux 3.7 Uncompressed Kernel Size: 14MB Compressed Size Decompression Speed LZO 6.0MB 34.1MB/s, 52.2MB/s(UA) LZ4 6.5MB 86.7MB/s - UA: Unaligned memory Access support - Latest patch set for LZO applied This patch set is for adding support for LZ4-compressed Kernel. LZ4 is a very fast lossless compression algorithm and it also features an extremely fast decoder [1]. But we have five of decompressors already and one question which does arise, however, is that of where do we stop adding new ones? This issue had been discussed and came to the conclusion [2]. Russell King said that we should have: - one decompressor which is the fastest - one decompressor for the highest compression ratio - one popular decompressor (eg conventional gzip) If we have a replacement one for one of these, then it should do exactly that: replace it. The benchmark shows that an 8% increase in image size vs a 66% increase in decompression speed compared to LZO(which has been known as the fastest decompressor in the Kernel). Therefore the "fast but may not be small" compression title has clearly been taken by LZ4 [3]. [1] http://code.google.com/p/lz4/ [2] http://thread.gmane.org/gmane.linux.kbuild.devel/9157 [3] http://thread.gmane.org/gmane.linux.kbuild.devel/9347 LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html LZ4 source repository: http://code.google.com/p/lz4/ Signed-off-by: Kyungsik Lee Signed-off-by: Yann Collet Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Russell King Cc: Borislav Petkov Cc: Florian Fainelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Change-Id: I1cf752fa48886aeff3c8732e3413f53c82b30e56 Reviewed-on: http://gerrit.mot.com/694981 Submit-Approved: Jira Key Tested-by: Jira Key Reviewed-by: Jeffrey Carlyle SLTApproved: Christopher Fries (cherry picked from commit b94a1d3f57d659195911077b087a9f7661492812) --- include/linux/lz4.h | 51 ++++++ lib/lz4/lz4_decompress.c | 326 +++++++++++++++++++++++++++++++++++++++ lib/lz4/lz4defs.h | 94 +++++++++++ 3 files changed, 471 insertions(+) create mode 100644 include/linux/lz4.h create mode 100644 lib/lz4/lz4_decompress.c create mode 100644 lib/lz4/lz4defs.h diff --git a/include/linux/lz4.h b/include/linux/lz4.h new file mode 100644 index 000000000000..7f6c75a093f8 --- /dev/null +++ b/include/linux/lz4.h @@ -0,0 +1,51 @@ +#ifndef __LZ4_H__ +#define __LZ4_H__ +/* + * LZ4 Kernel Interface + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * lz4_compressbound() + * Provides the maximum size that LZ4 may output in a "worst case" scenario + * (input data not compressible) + */ +static inline size_t lz4_compressbound(size_t isize) +{ + return isize + (isize / 255) + 16; +} + +/* + * lz4_decompress() + * src : source address of the compressed data + * src_len : is the input size, whcih is returned after decompress done + * dest : output buffer address of the decompressed data + * actual_dest_len: is the size of uncompressed data, supposing it's known + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer must be already allocated. + * slightly faster than lz4_decompress_unknownoutputsize() + */ +int lz4_decompress(const char *src, size_t *src_len, char *dest, + size_t actual_dest_len); + +/* + * lz4_decompress_unknownoutputsize() + * src : source address of the compressed data + * src_len : is the input size, therefore the compressed size + * dest : output buffer address of the decompressed data + * dest_len: is the max size of the destination buffer, which is + * returned with actual size of decompressed data after + * decompress done + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer must be already allocated. + */ +int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, + char *dest, size_t *dest_len); +#endif diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c new file mode 100644 index 000000000000..dcc89753af65 --- /dev/null +++ b/lib/lz4/lz4_decompress.c @@ -0,0 +1,326 @@ +/* + * LZ4 Decompressor for Linux kernel + * + * Copyright (C) 2013 LG Electronics Co., Ltd. (http://www.lge.com/) + * + * Based on LZ4 implementation by Yann Collet. + * + * LZ4 - Fast LZ compression algorithm + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + */ + +#ifndef STATIC +#include +#include +#endif +#include + +#include + +#include "lz4defs.h" + +static int lz4_uncompress(const char *source, char *dest, int osize) +{ + const BYTE *ip = (const BYTE *) source; + const BYTE *ref; + BYTE *op = (BYTE *) dest; + BYTE * const oend = op + osize; + BYTE *cpy; + unsigned token; + size_t length; + size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; +#if LZ4_ARCH64 + size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; +#endif + + while (1) { + + /* get runlength */ + token = *ip++; + length = (token >> ML_BITS); + if (length == RUN_MASK) { + size_t len; + + len = *ip++; + for (; len == 255; length += 255) + len = *ip++; + length += len; + } + + /* copy literals */ + cpy = op + length; + if (unlikely(cpy > oend - COPYLENGTH)) { + /* + * Error: not enough place for another match + * (min 4) + 5 literals + */ + if (cpy != oend) + goto _output_error; + + memcpy(op, ip, length); + ip += length; + break; /* EOF */ + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + + /* Error: offset create reference outside destination buffer */ + if (unlikely(ref < (BYTE *const) dest)) + goto _output_error; + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) { + for (; *ip == 255; length += 255) + ip++; + length += *ip++; + } + + /* copy repeated sequence */ + if (unlikely((op - ref) < STEPSIZE)) { +#if LZ4_ARCH64 + size_t dec64 = dec64table[op - ref]; +#else + const int dec64 = 0; +#endif + op[0] = ref[0]; + op[1] = ref[1]; + op[2] = ref[2]; + op[3] = ref[3]; + op += 4; + ref += 4; + ref -= dec32table[op-ref]; + PUT4(ref, op); + op += STEPSIZE - 4; + ref -= dec64; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE - 4); + if (cpy > (oend - COPYLENGTH)) { + + /* Error: request to write beyond destination buffer */ + if (cpy > oend) + goto _output_error; + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + /* + * Check EOF (should never happen, since last 5 bytes + * are supposed to be literals) + */ + if (op == oend) + goto _output_error; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + /* end of decoding */ + return (int) (((char *)ip) - source); + + /* write overflow error detected */ +_output_error: + return (int) (-(((char *)ip) - source)); +} + +static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, + int isize, size_t maxoutputsize) +{ + const BYTE *ip = (const BYTE *) source; + const BYTE *const iend = ip + isize; + const BYTE *ref; + + + BYTE *op = (BYTE *) dest; + BYTE * const oend = op + maxoutputsize; + BYTE *cpy; + + size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0}; +#if LZ4_ARCH64 + size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; +#endif + + /* Main Loop */ + while (ip < iend) { + + unsigned token; + size_t length; + + /* get runlength */ + token = *ip++; + length = (token >> ML_BITS); + if (length == RUN_MASK) { + int s = 255; + while ((ip < iend) && (s == 255)) { + s = *ip++; + length += s; + } + } + /* copy literals */ + cpy = op + length; + if ((cpy > oend - COPYLENGTH) || + (ip + length > iend - COPYLENGTH)) { + + if (cpy > oend) + goto _output_error;/* writes beyond buffer */ + + if (ip + length != iend) + goto _output_error;/* + * Error: LZ4 format requires + * to consume all input + * at this stage + */ + memcpy(op, ip, length); + op += length; + break;/* Necessarily EOF, due to parsing restrictions */ + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + if (ref < (BYTE * const) dest) + goto _output_error; + /* + * Error : offset creates reference + * outside of destination buffer + */ + + /* get matchlength */ + length = (token & ML_MASK); + if (length == ML_MASK) { + while (ip < iend) { + int s = *ip++; + length += s; + if (s == 255) + continue; + break; + } + } + + /* copy repeated sequence */ + if (unlikely((op - ref) < STEPSIZE)) { +#if LZ4_ARCH64 + size_t dec64 = dec64table[op - ref]; +#else + const int dec64 = 0; +#endif + op[0] = ref[0]; + op[1] = ref[1]; + op[2] = ref[2]; + op[3] = ref[3]; + op += 4; + ref += 4; + ref -= dec32table[op - ref]; + PUT4(ref, op); + op += STEPSIZE - 4; + ref -= dec64; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE-4); + if (cpy > oend - COPYLENGTH) { + if (cpy > oend) + goto _output_error; /* write outside of buf */ + + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + /* + * Check EOF (should never happen, since last 5 bytes + * are supposed to be literals) + */ + if (op == oend) + goto _output_error; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + /* end of decoding */ + return (int) (((char *) op) - dest); + + /* write overflow error detected */ +_output_error: + return (int) (-(((char *) ip) - source)); +} + +int lz4_decompress(const char *src, size_t *src_len, char *dest, + size_t actual_dest_len) +{ + int ret = -1; + int input_len = 0; + + input_len = lz4_uncompress(src, dest, actual_dest_len); + if (input_len < 0) + goto exit_0; + *src_len = input_len; + + return 0; +exit_0: + return ret; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lz4_decompress); +#endif + +int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, + char *dest, size_t *dest_len) +{ + int ret = -1; + int out_len = 0; + + out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len, + *dest_len); + if (out_len < 0) + goto exit_0; + *dest_len = out_len; + + return 0; +exit_0: + return ret; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4 Decompressor"); +#endif diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h new file mode 100644 index 000000000000..43ac31d63f36 --- /dev/null +++ b/lib/lz4/lz4defs.h @@ -0,0 +1,94 @@ +/* + * lz4defs.h -- architecture specific defines + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Detects 64 bits mode + */ +#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \ + || defined(__ppc64__) || defined(__LP64__)) +#define LZ4_ARCH64 1 +#else +#define LZ4_ARCH64 0 +#endif + +/* + * Architecture-specific macros + */ +#define BYTE u8 +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \ + || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6 \ + && defined(ARM_EFFICIENT_UNALIGNED_ACCESS) +typedef struct _U32_S { u32 v; } U32_S; +typedef struct _U64_S { u64 v; } U64_S; + +#define A32(x) (((U32_S *)(x))->v) +#define A64(x) (((U64_S *)(x))->v) + +#define PUT4(s, d) (A32(d) = A32(s)) +#define PUT8(s, d) (A64(d) = A64(s)) +#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */ + +#define PUT4(s, d) \ + put_unaligned(get_unaligned((const u32 *) s), (u32 *) d) +#define PUT8(s, d) \ + put_unaligned(get_unaligned((const u64 *) s), (u64 *) d) +#endif + +#define COPYLENGTH 8 +#define ML_BITS 4 +#define ML_MASK ((1U << ML_BITS) - 1) +#define RUN_BITS (8 - ML_BITS) +#define RUN_MASK ((1U << RUN_BITS) - 1) + +#if LZ4_ARCH64/* 64-bit */ +#define STEPSIZE 8 + +#define LZ4_COPYSTEP(s, d) \ + do { \ + PUT8(s, d); \ + d += 8; \ + s += 8; \ + } while (0) + +#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d) + +#define LZ4_SECURECOPY(s, d, e) \ + do { \ + if (d < e) { \ + LZ4_WILDCOPY(s, d, e); \ + } \ + } while (0) + +#else /* 32-bit */ +#define STEPSIZE 4 + +#define LZ4_COPYSTEP(s, d) \ + do { \ + PUT4(s, d); \ + d += 4; \ + s += 4; \ + } while (0) + +#define LZ4_COPYPACKET(s, d) \ + do { \ + LZ4_COPYSTEP(s, d); \ + LZ4_COPYSTEP(s, d); \ + } while (0) + +#define LZ4_SECURECOPY LZ4_WILDCOPY +#endif + +#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \ + (d = s - get_unaligned_le16(p)) + +#define LZ4_WILDCOPY(s, d, e) \ + do { \ + LZ4_COPYPACKET(s, d); \ + } while (d < e) From 942ad0f73e7ddc3e16a685aae64af20d4324dff5 Mon Sep 17 00:00:00 2001 From: Kyungsik Lee Date: Mon, 8 Jul 2013 16:01:46 -0700 Subject: [PATCH 075/104] lib: add support for LZ4-compressed kernel Add support for extracting LZ4-compressed kernel images, as well as LZ4-compressed ramdisk images in the kernel boot process. Signed-off-by: Kyungsik Lee Cc: "H. Peter Anvin" Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Russell King Cc: Borislav Petkov Cc: Florian Fainelli Cc: Yann Collet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit d1eda0bf70a124b333c0a362e998eed6d2249eaf) --- include/linux/decompress/unlz4.h | 10 ++ init/Kconfig | 17 ++- lib/Kconfig | 7 ++ lib/Makefile | 2 + lib/decompress.c | 5 + lib/decompress_unlz4.c | 187 +++++++++++++++++++++++++++++++ lib/lz4/Makefile | 1 + lib/lz4/lz4_decompress.c | 2 +- scripts/Makefile.lib | 5 + usr/Kconfig | 9 ++ 10 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 include/linux/decompress/unlz4.h create mode 100644 lib/decompress_unlz4.c create mode 100644 lib/lz4/Makefile diff --git a/include/linux/decompress/unlz4.h b/include/linux/decompress/unlz4.h new file mode 100644 index 000000000000..d5b68bf3ec92 --- /dev/null +++ b/include/linux/decompress/unlz4.h @@ -0,0 +1,10 @@ +#ifndef DECOMPRESS_UNLZ4_H +#define DECOMPRESS_UNLZ4_H + +int unlz4(unsigned char *inbuf, int len, + int(*fill)(void*, unsigned int), + int(*flush)(void*, unsigned int), + unsigned char *output, + int *pos, + void(*error)(char *x)); +#endif diff --git a/init/Kconfig b/init/Kconfig index e4b67a1dddf0..11a5a227daa7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -130,10 +130,13 @@ config HAVE_KERNEL_XZ config HAVE_KERNEL_LZO bool +config HAVE_KERNEL_LZ4 + bool + choice prompt "Kernel compression mode" default KERNEL_GZIP - depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO + depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4 help The linux kernel is a kind of self-extracting executable. Several compression algorithms are available, which differ @@ -201,6 +204,18 @@ config KERNEL_LZO size is about 10% bigger than gzip; however its speed (both compression and decompression) is the fastest. +config KERNEL_LZ4 + bool "LZ4" + depends on HAVE_KERNEL_LZ4 + help + LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding. + A preliminary version of LZ4 de/compression tool is available at + . + + Its compression ratio is worse than LZO. The size of the kernel + is about 8% bigger than LZO. But the decompression speed is + faster than LZO. + endchoice config DEFAULT_HOSTNAME diff --git a/lib/Kconfig b/lib/Kconfig index 8437e3692478..dd2ed4980bd8 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -177,6 +177,9 @@ config LZO_COMPRESS config LZO_DECOMPRESS tristate +config LZ4_DECOMPRESS + tristate + source "lib/xz/Kconfig" # @@ -201,6 +204,10 @@ config DECOMPRESS_LZO select LZO_DECOMPRESS tristate +config DECOMPRESS_LZ4 + select LZ4_DECOMPRESS + tristate + # # Generic allocator support is selected if needed # diff --git a/lib/Makefile b/lib/Makefile index 0fbcb04fde58..4b4134d07771 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_LZO_COMPRESS) += lzo/ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ +obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_XZ_DEC) += xz/ obj-$(CONFIG_RAID6_PQ) += raid6/ @@ -79,6 +80,7 @@ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o +lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o obj-$(CONFIG_TEXTSEARCH) += textsearch.o obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o diff --git a/lib/decompress.c b/lib/decompress.c index 3d766b7f60ab..fc3f2dda8c3e 100644 --- a/lib/decompress.c +++ b/lib/decompress.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,9 @@ #ifndef CONFIG_DECOMPRESS_LZO # define unlzo NULL #endif +#ifndef CONFIG_DECOMPRESS_LZ4 +# define unlz4 NULL +#endif static const struct compress_format { unsigned char magic[2]; @@ -42,6 +46,7 @@ static const struct compress_format { { {0x5d, 0x00}, "lzma", unlzma }, { {0xfd, 0x37}, "xz", unxz }, { {0x89, 0x4c}, "lzo", unlzo }, + { {0x02, 0x21}, "lz4", unlz4 }, { {0, 0}, NULL, NULL } }; diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c new file mode 100644 index 000000000000..3e67cfad16ad --- /dev/null +++ b/lib/decompress_unlz4.c @@ -0,0 +1,187 @@ +/* + * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd + * + * Copyright (C) 2013, LG Electronics, Kyungsik Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef STATIC +#define PREBOOT +#include "lz4/lz4_decompress.c" +#else +#include +#endif +#include +#include +#include +#include + +#include + +/* + * Note: Uncompressed chunk size is used in the compressor side + * (userspace side for compression). + * It is hardcoded because there is not proper way to extract it + * from the binary stream which is generated by the preliminary + * version of LZ4 tool so far. + */ +#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) +#define ARCHIVE_MAGICNUMBER 0x184C2102 + +STATIC inline int INIT unlz4(u8 *input, int in_len, + int (*fill) (void *, unsigned int), + int (*flush) (void *, unsigned int), + u8 *output, int *posp, + void (*error) (char *x)) +{ + int ret = -1; + size_t chunksize = 0; + size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; + u8 *inp; + u8 *inp_start; + u8 *outp; + int size = in_len; +#ifdef PREBOOT + size_t out_len = get_unaligned_le32(input + in_len); +#endif + size_t dest_len; + + + if (output) { + outp = output; + } else if (!flush) { + error("NULL output pointer and no flush function provided"); + goto exit_0; + } else { + outp = large_malloc(uncomp_chunksize); + if (!outp) { + error("Could not allocate output buffer"); + goto exit_0; + } + } + + if (input && fill) { + error("Both input pointer and fill function provided,"); + goto exit_1; + } else if (input) { + inp = input; + } else if (!fill) { + error("NULL input pointer and missing fill function"); + goto exit_1; + } else { + inp = large_malloc(lz4_compressbound(uncomp_chunksize)); + if (!inp) { + error("Could not allocate input buffer"); + goto exit_1; + } + } + inp_start = inp; + + if (posp) + *posp = 0; + + if (fill) + fill(inp, 4); + + chunksize = get_unaligned_le32(inp); + if (chunksize == ARCHIVE_MAGICNUMBER) { + inp += 4; + size -= 4; + } else { + error("invalid header"); + goto exit_2; + } + + if (posp) + *posp += 4; + + for (;;) { + + if (fill) + fill(inp, 4); + + chunksize = get_unaligned_le32(inp); + if (chunksize == ARCHIVE_MAGICNUMBER) { + inp += 4; + size -= 4; + if (posp) + *posp += 4; + continue; + } + inp += 4; + size -= 4; + + if (posp) + *posp += 4; + + if (fill) { + if (chunksize > lz4_compressbound(uncomp_chunksize)) { + error("chunk length is longer than allocated"); + goto exit_2; + } + fill(inp, chunksize); + } +#ifdef PREBOOT + if (out_len >= uncomp_chunksize) { + dest_len = uncomp_chunksize; + out_len -= dest_len; + } else + dest_len = out_len; + ret = lz4_decompress(inp, &chunksize, outp, dest_len); +#else + dest_len = uncomp_chunksize; + ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp, + &dest_len); +#endif + if (ret < 0) { + error("Decoding failed"); + goto exit_2; + } + + if (flush && flush(outp, dest_len) != dest_len) + goto exit_2; + if (output) + outp += dest_len; + if (posp) + *posp += chunksize; + + size -= chunksize; + + if (size == 0) + break; + else if (size < 0) { + error("data corrupted"); + goto exit_2; + } + + inp += chunksize; + if (fill) + inp = inp_start; + } + + ret = 0; +exit_2: + if (!input) + large_free(inp_start); +exit_1: + if (!output) + large_free(outp); +exit_0: + return ret; +} + +#ifdef PREBOOT +STATIC int INIT decompress(unsigned char *buf, int in_len, + int(*fill)(void*, unsigned int), + int(*flush)(void*, unsigned int), + unsigned char *output, + int *posp, + void(*error)(char *x) + ) +{ + return unlz4(buf, in_len - 4, fill, flush, output, posp, error); +} +#endif diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile new file mode 100644 index 000000000000..7f548c6d1c5c --- /dev/null +++ b/lib/lz4/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index dcc89753af65..d3414eae73a1 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -1,7 +1,7 @@ /* * LZ4 Decompressor for Linux kernel * - * Copyright (C) 2013 LG Electronics Co., Ltd. (http://www.lge.com/) + * Copyright (C) 2013, LG Electronics, Kyungsik Lee * * Based on LZ4 implementation by Yann Collet. * diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index b1dd35069a02..bfab2a235161 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -310,6 +310,11 @@ cmd_lzo = (cat $(filter-out FORCE,$^) | \ lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ (rm -f $@ ; false) +quiet_cmd_lz4 = LZ4 $@ +cmd_lz4 = (cat $(filter-out FORCE,$^) | \ + lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ || \ + (rm -f $@ ; false) + # U-Boot mkimage # --------------------------------------------------------------------------- diff --git a/usr/Kconfig b/usr/Kconfig index 65b845bd4e3e..16ffe99bbade 100644 --- a/usr/Kconfig +++ b/usr/Kconfig @@ -90,6 +90,15 @@ config RD_LZO Support loading of a LZO encoded initial ramdisk or cpio buffer If unsure, say N. +config RD_LZ4 + bool "Support initial ramdisks compressed using LZ4" if EXPERT + default !EXPERT + depends on BLK_DEV_INITRD + select DECOMPRESS_LZ4 + help + Support loading of a LZ4 encoded initial ramdisk or cpio buffer + If unsure, say N. + choice prompt "Built-in initramfs compression mode" if INITRAMFS_SOURCE!="" help From e6669d8fab51156deb994bcebf8adb6b255c7bd2 Mon Sep 17 00:00:00 2001 From: Chanho Min Date: Mon, 8 Jul 2013 16:01:49 -0700 Subject: [PATCH 076/104] lib: add lz4 compressor module This patchset is for supporting LZ4 compression and the crypto API using it. As shown below, the size of data is a little bit bigger but compressing speed is faster under the enabled unaligned memory access. We can use lz4 de/compression through crypto API as well. Also, It will be useful for another potential user of lz4 compression. lz4 Compression Benchmark: Compiler: ARM gcc 4.6.4 ARMv7, 1 GHz based board Kernel: linux 3.4 Uncompressed data Size: 101 MB Compressed Size compression Speed LZO 72.1MB 32.1MB/s, 33.0MB/s(UA) LZ4 75.1MB 30.4MB/s, 35.9MB/s(UA) LZ4HC 59.8MB 2.4MB/s, 2.5MB/s(UA) - UA: Unaligned memory Access support - Latest patch set for LZO applied This patch: Add support for LZ4 compression in the Linux Kernel. LZ4 Compression APIs for kernel are based on LZ4 implementation by Yann Collet and were changed for kernel coding style. LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html LZ4 source repository : http://code.google.com/p/lz4/ svn revision : r90 Two APIs are added: lz4_compress() support basic lz4 compression whereas lz4hc_compress() support high compression or CPU performance get lower but compression ratio get higher. Also, we require the pre-allocated working memory with the defined size and destination buffer must be allocated with the size of lz4_compressbound. [akpm@linux-foundation.org: make lz4_compresshcctx() static] Signed-off-by: Chanho Min Cc: "Darrick J. Wong" Cc: Bob Pearson Cc: Richard Weinberger Cc: Herbert Xu Cc: Yann Collet Cc: Kyungsik Lee Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit ae54eec1285f644c7acec9c4c0c3963178e93c3f) --- include/linux/lz4.h | 36 +++ lib/Kconfig | 6 + lib/Makefile | 2 + lib/lz4/Makefile | 2 + lib/lz4/lz4_compress.c | 443 ++++++++++++++++++++++++++++++++ lib/lz4/lz4defs.h | 66 ++++- lib/lz4/lz4hc_compress.c | 539 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 1092 insertions(+), 2 deletions(-) create mode 100644 lib/lz4/lz4_compress.c create mode 100644 lib/lz4/lz4hc_compress.c diff --git a/include/linux/lz4.h b/include/linux/lz4.h index 7f6c75a093f8..d21c13f10a64 100644 --- a/include/linux/lz4.h +++ b/include/linux/lz4.h @@ -9,6 +9,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define LZ4_MEM_COMPRESS (4096 * sizeof(unsigned char *)) +#define LZ4HC_MEM_COMPRESS (65538 * sizeof(unsigned char *)) /* * lz4_compressbound() @@ -20,6 +22,40 @@ static inline size_t lz4_compressbound(size_t isize) return isize + (isize / 255) + 16; } +/* + * lz4_compress() + * src : source address of the original data + * src_len : size of the original data + * dst : output buffer address of the compressed data + * This requires 'dst' of size LZ4_COMPRESSBOUND. + * dst_len : is the output size, which is returned after compress done + * workmem : address of the working memory. + * This requires 'workmem' of size LZ4_MEM_COMPRESS. + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer and workmem must be already allocated with + * the defined size. + */ +int lz4_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem); + + /* + * lz4hc_compress() + * src : source address of the original data + * src_len : size of the original data + * dst : output buffer address of the compressed data + * This requires 'dst' of size LZ4_COMPRESSBOUND. + * dst_len : is the output size, which is returned after compress done + * workmem : address of the working memory. + * This requires 'workmem' of size LZ4HC_MEM_COMPRESS. + * return : Success if return 0 + * Error if return (< 0) + * note : Destination buffer and workmem must be already allocated with + * the defined size. + */ +int lz4hc_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem); + /* * lz4_decompress() * src : source address of the compressed data diff --git a/lib/Kconfig b/lib/Kconfig index dd2ed4980bd8..d4636b13a385 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -177,6 +177,12 @@ config LZO_COMPRESS config LZO_DECOMPRESS tristate +config LZ4_COMPRESS + tristate + +config LZ4HC_COMPRESS + tristate + config LZ4_DECOMPRESS tristate diff --git a/lib/Makefile b/lib/Makefile index 4b4134d07771..c5f2b3ba397a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -71,6 +71,8 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/ obj-$(CONFIG_BCH) += bch.o obj-$(CONFIG_LZO_COMPRESS) += lzo/ obj-$(CONFIG_LZO_DECOMPRESS) += lzo/ +obj-$(CONFIG_LZ4_COMPRESS) += lz4/ +obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_XZ_DEC) += xz/ obj-$(CONFIG_RAID6_PQ) += raid6/ diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile index 7f548c6d1c5c..8085d04e9309 100644 --- a/lib/lz4/Makefile +++ b/lib/lz4/Makefile @@ -1 +1,3 @@ +obj-$(CONFIG_LZ4_COMPRESS) += lz4_compress.o +obj-$(CONFIG_LZ4HC_COMPRESS) += lz4hc_compress.o obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c new file mode 100644 index 000000000000..fd94058bd7f9 --- /dev/null +++ b/lib/lz4/lz4_compress.c @@ -0,0 +1,443 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + * + * Changed for kernel use by: + * Chanho Min + */ + +#include +#include +#include +#include +#include "lz4defs.h" + +/* + * LZ4_compressCtx : + * ----------------- + * Compress 'isize' bytes from 'source' into an output buffer 'dest' of + * maximum size 'maxOutputSize'. * If it cannot achieve it, compression + * will stop, and result of the function will be zero. + * return : the number of bytes written in buffer 'dest', or 0 if the + * compression fails + */ +static inline int lz4_compressctx(void *ctx, + const char *source, + char *dest, + int isize, + int maxoutputsize) +{ + HTYPE *hashtable = (HTYPE *)ctx; + const u8 *ip = (u8 *)source; +#if LZ4_ARCH64 + const BYTE * const base = ip; +#else + const int base = 0; +#endif + const u8 *anchor = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + #define MATCHLIMIT (iend - LASTLITERALS) + + u8 *op = (u8 *) dest; + u8 *const oend = op + maxoutputsize; + int length; + const int skipstrength = SKIPSTRENGTH; + u32 forwardh; + int lastrun; + + /* Init */ + if (isize < MINLENGTH) + goto _last_literals; + + memset((void *)hashtable, 0, LZ4_MEM_COMPRESS); + + /* First Byte */ + hashtable[LZ4_HASH_VALUE(ip)] = ip - base; + ip++; + forwardh = LZ4_HASH_VALUE(ip); + + /* Main Loop */ + for (;;) { + int findmatchattempts = (1U << skipstrength) + 3; + const u8 *forwardip = ip; + const u8 *ref; + u8 *token; + + /* Find a match */ + do { + u32 h = forwardh; + int step = findmatchattempts++ >> skipstrength; + ip = forwardip; + forwardip = ip + step; + + if (unlikely(forwardip > mflimit)) + goto _last_literals; + + forwardh = LZ4_HASH_VALUE(forwardip); + ref = base + hashtable[h]; + hashtable[h] = ip - base; + } while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip))); + + /* Catch up */ + while ((ip > anchor) && (ref > (u8 *)source) && + unlikely(ip[-1] == ref[-1])) { + ip--; + ref--; + } + + /* Encode Literal length */ + length = (int)(ip - anchor); + token = op++; + /* check output limit */ + if (unlikely(op + length + (2 + 1 + LASTLITERALS) + + (length >> 8) > oend)) + return 0; + + if (length >= (int)RUN_MASK) { + int len; + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *op++ = 255; + *op++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(anchor, op, length); +_next_match: + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref)); + + /* Start Counting */ + ip += MINMATCH; + /* MinMatch verified */ + ref += MINMATCH; + anchor = ip; + while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) { + #if LZ4_ARCH64 + u64 diff = A64(ref) ^ A64(ip); + #else + u32 diff = A32(ref) ^ A32(ip); + #endif + if (!diff) { + ip += STEPSIZE; + ref += STEPSIZE; + continue; + } + ip += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) { + ip += 4; + ref += 4; + } + #endif + if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) { + ip += 2; + ref += 2; + } + if ((ip < MATCHLIMIT) && (*ref == *ip)) + ip++; +_endcount: + /* Encode MatchLength */ + length = (int)(ip - anchor); + /* Check output limit */ + if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend)) + return 0; + if (length >= (int)ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for (; length > 509 ; length -= 510) { + *op++ = 255; + *op++ = 255; + } + if (length > 254) { + length -= 255; + *op++ = 255; + } + *op++ = (u8)length; + } else + *token += length; + + /* Test end of chunk */ + if (ip > mflimit) { + anchor = ip; + break; + } + + /* Fill table */ + hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base; + + /* Test next position */ + ref = base + hashtable[LZ4_HASH_VALUE(ip)]; + hashtable[LZ4_HASH_VALUE(ip)] = ip - base; + if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + anchor = ip++; + forwardh = LZ4_HASH_VALUE(ip); + } + +_last_literals: + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (((char *)op - dest) + lastrun + 1 + + ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize) + return 0; + + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8)lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + + /* End */ + return (int)(((char *)op) - dest); +} + +static inline int lz4_compress64kctx(void *ctx, + const char *source, + char *dest, + int isize, + int maxoutputsize) +{ + u16 *hashtable = (u16 *)ctx; + const u8 *ip = (u8 *) source; + const u8 *anchor = ip; + const u8 *const base = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + #define MATCHLIMIT (iend - LASTLITERALS) + + u8 *op = (u8 *) dest; + u8 *const oend = op + maxoutputsize; + int len, length; + const int skipstrength = SKIPSTRENGTH; + u32 forwardh; + int lastrun; + + /* Init */ + if (isize < MINLENGTH) + goto _last_literals; + + memset((void *)hashtable, 0, LZ4_MEM_COMPRESS); + + /* First Byte */ + ip++; + forwardh = LZ4_HASH64K_VALUE(ip); + + /* Main Loop */ + for (;;) { + int findmatchattempts = (1U << skipstrength) + 3; + const u8 *forwardip = ip; + const u8 *ref; + u8 *token; + + /* Find a match */ + do { + u32 h = forwardh; + int step = findmatchattempts++ >> skipstrength; + ip = forwardip; + forwardip = ip + step; + + if (forwardip > mflimit) + goto _last_literals; + + forwardh = LZ4_HASH64K_VALUE(forwardip); + ref = base + hashtable[h]; + hashtable[h] = (u16)(ip - base); + } while (A32(ref) != A32(ip)); + + /* Catch up */ + while ((ip > anchor) && (ref > (u8 *)source) + && (ip[-1] == ref[-1])) { + ip--; + ref--; + } + + /* Encode Literal length */ + length = (int)(ip - anchor); + token = op++; + /* Check output limit */ + if (unlikely(op + length + (2 + 1 + LASTLITERALS) + + (length >> 8) > oend)) + return 0; + if (length >= (int)RUN_MASK) { + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *op++ = 255; + *op++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(anchor, op, length); + +_next_match: + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref)); + + /* Start Counting */ + ip += MINMATCH; + /* MinMatch verified */ + ref += MINMATCH; + anchor = ip; + + while (ip < MATCHLIMIT - (STEPSIZE - 1)) { + #if LZ4_ARCH64 + u64 diff = A64(ref) ^ A64(ip); + #else + u32 diff = A32(ref) ^ A32(ip); + #endif + + if (!diff) { + ip += STEPSIZE; + ref += STEPSIZE; + continue; + } + ip += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) { + ip += 4; + ref += 4; + } + #endif + if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) { + ip += 2; + ref += 2; + } + if ((ip < MATCHLIMIT) && (*ref == *ip)) + ip++; +_endcount: + + /* Encode MatchLength */ + len = (int)(ip - anchor); + /* Check output limit */ + if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend)) + return 0; + if (len >= (int)ML_MASK) { + *token += ML_MASK; + len -= ML_MASK; + for (; len > 509 ; len -= 510) { + *op++ = 255; + *op++ = 255; + } + if (len > 254) { + len -= 255; + *op++ = 255; + } + *op++ = (u8)len; + } else + *token += len; + + /* Test end of chunk */ + if (ip > mflimit) { + anchor = ip; + break; + } + + /* Fill table */ + hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base); + + /* Test next position */ + ref = base + hashtable[LZ4_HASH64K_VALUE(ip)]; + hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base); + if (A32(ref) == A32(ip)) { + token = op++; + *token = 0; + goto _next_match; + } + + /* Prepare next loop */ + anchor = ip++; + forwardh = LZ4_HASH64K_VALUE(ip); + } + +_last_literals: + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend) + return 0; + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8)lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + /* End */ + return (int)(((char *)op) - dest); +} + +int lz4_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem) +{ + int ret = -1; + int out_len = 0; + + if (src_len < LZ4_64KLIMIT) + out_len = lz4_compress64kctx(wrkmem, src, dst, src_len, + lz4_compressbound(src_len)); + else + out_len = lz4_compressctx(wrkmem, src, dst, src_len, + lz4_compressbound(src_len)); + + if (out_len < 0) + goto exit; + + *dst_len = out_len; + + return 0; +exit: + return ret; +} +EXPORT_SYMBOL_GPL(lz4_compress); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4 compressor"); diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h index 43ac31d63f36..abcecdc2d0f2 100644 --- a/lib/lz4/lz4defs.h +++ b/lib/lz4/lz4defs.h @@ -22,23 +22,40 @@ * Architecture-specific macros */ #define BYTE u8 +typedef struct _U16_S { u16 v; } U16_S; +typedef struct _U32_S { u32 v; } U32_S; +typedef struct _U64_S { u64 v; } U64_S; #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \ || defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6 \ && defined(ARM_EFFICIENT_UNALIGNED_ACCESS) -typedef struct _U32_S { u32 v; } U32_S; -typedef struct _U64_S { u64 v; } U64_S; +#define A16(x) (((U16_S *)(x))->v) #define A32(x) (((U32_S *)(x))->v) #define A64(x) (((U64_S *)(x))->v) #define PUT4(s, d) (A32(d) = A32(s)) #define PUT8(s, d) (A64(d) = A64(s)) +#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \ + do { \ + A16(p) = v; \ + p += 2; \ + } while (0) #else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */ +#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v)) +#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v)) +#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v)) + #define PUT4(s, d) \ put_unaligned(get_unaligned((const u32 *) s), (u32 *) d) #define PUT8(s, d) \ put_unaligned(get_unaligned((const u64 *) s), (u64 *) d) + +#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \ + do { \ + put_unaligned(v, (u16 *)(p)); \ + p += 2; \ + } while (0) #endif #define COPYLENGTH 8 @@ -46,6 +63,29 @@ typedef struct _U64_S { u64 v; } U64_S; #define ML_MASK ((1U << ML_BITS) - 1) #define RUN_BITS (8 - ML_BITS) #define RUN_MASK ((1U << RUN_BITS) - 1) +#define MEMORY_USAGE 14 +#define MINMATCH 4 +#define SKIPSTRENGTH 6 +#define LASTLITERALS 5 +#define MFLIMIT (COPYLENGTH + MINMATCH) +#define MINLENGTH (MFLIMIT + 1) +#define MAXD_LOG 16 +#define MAXD (1 << MAXD_LOG) +#define MAXD_MASK (u32)(MAXD - 1) +#define MAX_DISTANCE (MAXD - 1) +#define HASH_LOG (MAXD_LOG - 1) +#define HASHTABLESIZE (1 << HASH_LOG) +#define MAX_NB_ATTEMPTS 256 +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) +#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT - 1)) +#define HASHLOG64K ((MEMORY_USAGE - 2) + 1) +#define HASH64KTABLESIZE (1U << HASHLOG64K) +#define LZ4_HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - (MEMORY_USAGE-2))) +#define LZ4_HASH64K_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - HASHLOG64K)) +#define HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \ + ((MINMATCH * 8) - HASH_LOG)) #if LZ4_ARCH64/* 64-bit */ #define STEPSIZE 8 @@ -65,6 +105,13 @@ typedef struct _U64_S { u64 v; } U64_S; LZ4_WILDCOPY(s, d, e); \ } \ } while (0) +#define HTYPE u32 + +#ifdef __BIG_ENDIAN +#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3) +#else +#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3) +#endif #else /* 32-bit */ #define STEPSIZE 4 @@ -83,6 +130,14 @@ typedef struct _U64_S { u64 v; } U64_S; } while (0) #define LZ4_SECURECOPY LZ4_WILDCOPY +#define HTYPE const u8* + +#ifdef __BIG_ENDIAN +#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3) +#else +#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3) +#endif + #endif #define LZ4_READ_LITTLEENDIAN_16(d, s, p) \ @@ -92,3 +147,10 @@ typedef struct _U64_S { u64 v; } U64_S; do { \ LZ4_COPYPACKET(s, d); \ } while (d < e) + +#define LZ4_BLINDCOPY(s, d, l) \ + do { \ + u8 *e = (d) + l; \ + LZ4_WILDCOPY(s, d, e); \ + d = e; \ + } while (0) diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c new file mode 100644 index 000000000000..eb1a74f5e368 --- /dev/null +++ b/lib/lz4/lz4hc_compress.c @@ -0,0 +1,539 @@ +/* + * LZ4 HC - High Compression Mode of LZ4 + * Copyright (C) 2011-2012, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + * + * Changed for kernel use by: + * Chanho Min + */ + +#include +#include +#include +#include +#include "lz4defs.h" + +struct lz4hc_data { + const u8 *base; + HTYPE hashtable[HASHTABLESIZE]; + u16 chaintable[MAXD]; + const u8 *nexttoupdate; +} __attribute__((__packed__)); + +static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base) +{ + memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable)); + memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable)); + +#if LZ4_ARCH64 + hc4->nexttoupdate = base + 1; +#else + hc4->nexttoupdate = base; +#endif + hc4->base = base; + return 1; +} + +/* Update chains up to ip (excluded) */ +static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip) +{ + u16 *chaintable = hc4->chaintable; + HTYPE *hashtable = hc4->hashtable; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + + while (hc4->nexttoupdate < ip) { + const u8 *p = hc4->nexttoupdate; + size_t delta = p - (hashtable[HASH_VALUE(p)] + base); + if (delta > MAX_DISTANCE) + delta = MAX_DISTANCE; + chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta; + hashtable[HASH_VALUE(p)] = (p) - base; + hc4->nexttoupdate++; + } +} + +static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2, + const u8 *const matchlimit) +{ + const u8 *p1t = p1; + + while (p1t < matchlimit - (STEPSIZE - 1)) { +#if LZ4_ARCH64 + u64 diff = A64(p2) ^ A64(p1t); +#else + u32 diff = A32(p2) ^ A32(p1t); +#endif + if (!diff) { + p1t += STEPSIZE; + p2 += STEPSIZE; + continue; + } + p1t += LZ4_NBCOMMONBYTES(diff); + return p1t - p1; + } +#if LZ4_ARCH64 + if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) { + p1t += 4; + p2 += 4; + } +#endif + + if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) { + p1t += 2; + p2 += 2; + } + if ((p1t < matchlimit) && (*p2 == *p1t)) + p1t++; + return p1t - p1; +} + +static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4, + const u8 *ip, const u8 *const matchlimit, const u8 **matchpos) +{ + u16 *const chaintable = hc4->chaintable; + HTYPE *const hashtable = hc4->hashtable; + const u8 *ref; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + int nbattempts = MAX_NB_ATTEMPTS; + size_t repl = 0, ml = 0; + u16 delta; + + /* HC4 match finder */ + lz4hc_insert(hc4, ip); + ref = hashtable[HASH_VALUE(ip)] + base; + + /* potential repetition */ + if (ref >= ip-4) { + /* confirmed */ + if (A32(ref) == A32(ip)) { + delta = (u16)(ip-ref); + repl = ml = lz4hc_commonlength(ip + MINMATCH, + ref + MINMATCH, matchlimit) + MINMATCH; + *matchpos = ref; + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + + while ((ref >= ip - MAX_DISTANCE) && nbattempts) { + nbattempts--; + if (*(ref + ml) == *(ip + ml)) { + if (A32(ref) == A32(ip)) { + size_t mlt = + lz4hc_commonlength(ip + MINMATCH, + ref + MINMATCH, matchlimit) + MINMATCH; + if (mlt > ml) { + ml = mlt; + *matchpos = ref; + } + } + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + + /* Complete table */ + if (repl) { + const BYTE *ptr = ip; + const BYTE *end; + end = ip + repl - (MINMATCH-1); + /* Pre-Load */ + while (ptr < end - delta) { + chaintable[(size_t)(ptr) & MAXD_MASK] = delta; + ptr++; + } + do { + chaintable[(size_t)(ptr) & MAXD_MASK] = delta; + /* Head of chain */ + hashtable[HASH_VALUE(ptr)] = (ptr) - base; + ptr++; + } while (ptr < end); + hc4->nexttoupdate = end; + } + + return (int)ml; +} + +static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4, + const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest, + const u8 **matchpos, const u8 **startpos) +{ + u16 *const chaintable = hc4->chaintable; + HTYPE *const hashtable = hc4->hashtable; +#if LZ4_ARCH64 + const BYTE * const base = hc4->base; +#else + const int base = 0; +#endif + const u8 *ref; + int nbattempts = MAX_NB_ATTEMPTS; + int delta = (int)(ip - startlimit); + + /* First Match */ + lz4hc_insert(hc4, ip); + ref = hashtable[HASH_VALUE(ip)] + base; + + while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base) + && (nbattempts)) { + nbattempts--; + if (*(startlimit + longest) == *(ref - delta + longest)) { + if (A32(ref) == A32(ip)) { + const u8 *reft = ref + MINMATCH; + const u8 *ipt = ip + MINMATCH; + const u8 *startt = ip; + + while (ipt < matchlimit-(STEPSIZE - 1)) { + #if LZ4_ARCH64 + u64 diff = A64(reft) ^ A64(ipt); + #else + u32 diff = A32(reft) ^ A32(ipt); + #endif + + if (!diff) { + ipt += STEPSIZE; + reft += STEPSIZE; + continue; + } + ipt += LZ4_NBCOMMONBYTES(diff); + goto _endcount; + } + #if LZ4_ARCH64 + if ((ipt < (matchlimit - 3)) + && (A32(reft) == A32(ipt))) { + ipt += 4; + reft += 4; + } + ipt += 2; + #endif + if ((ipt < (matchlimit - 1)) + && (A16(reft) == A16(ipt))) { + reft += 2; + } + if ((ipt < matchlimit) && (*reft == *ipt)) + ipt++; +_endcount: + reft = ref; + + while ((startt > startlimit) + && (reft > hc4->base) + && (startt[-1] == reft[-1])) { + startt--; + reft--; + } + + if ((ipt - startt) > longest) { + longest = (int)(ipt - startt); + *matchpos = reft; + *startpos = startt; + } + } + } + ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK]; + } + return longest; +} + +static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor, + int ml, const u8 *ref) +{ + int length, len; + u8 *token; + + /* Encode Literal length */ + length = (int)(*ip - *anchor); + token = (*op)++; + if (length >= (int)RUN_MASK) { + *token = (RUN_MASK << ML_BITS); + len = length - RUN_MASK; + for (; len > 254 ; len -= 255) + *(*op)++ = 255; + *(*op)++ = (u8)len; + } else + *token = (length << ML_BITS); + + /* Copy Literals */ + LZ4_BLINDCOPY(*anchor, *op, length); + + /* Encode Offset */ + LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref)); + + /* Encode MatchLength */ + len = (int)(ml - MINMATCH); + if (len >= (int)ML_MASK) { + *token += ML_MASK; + len -= ML_MASK; + for (; len > 509 ; len -= 510) { + *(*op)++ = 255; + *(*op)++ = 255; + } + if (len > 254) { + len -= 255; + *(*op)++ = 255; + } + *(*op)++ = (u8)len; + } else + *token += len; + + /* Prepare next loop */ + *ip += ml; + *anchor = *ip; + + return 0; +} + +static int lz4_compresshcctx(struct lz4hc_data *ctx, + const char *source, + char *dest, + int isize) +{ + const u8 *ip = (const u8 *)source; + const u8 *anchor = ip; + const u8 *const iend = ip + isize; + const u8 *const mflimit = iend - MFLIMIT; + const u8 *const matchlimit = (iend - LASTLITERALS); + + u8 *op = (u8 *)dest; + + int ml, ml2, ml3, ml0; + const u8 *ref = NULL; + const u8 *start2 = NULL; + const u8 *ref2 = NULL; + const u8 *start3 = NULL; + const u8 *ref3 = NULL; + const u8 *start0; + const u8 *ref0; + int lastrun; + + ip++; + + /* Main Loop */ + while (ip < mflimit) { + ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref)); + if (!ml) { + ip++; + continue; + } + + /* saved, in case we would skip too much */ + start0 = ip; + ref0 = ref; + ml0 = ml; +_search2: + if (ip+ml < mflimit) + ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2, + ip + 1, matchlimit, ml, &ref2, &start2); + else + ml2 = ml; + /* No better match */ + if (ml2 == ml) { + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + continue; + } + + if (start0 < ip) { + /* empirical */ + if (start2 < ip + ml0) { + ip = start0; + ref = ref0; + ml = ml0; + } + } + /* + * Here, start0==ip + * First Match too small : removed + */ + if ((start2 - ip) < 3) { + ml = ml2; + ip = start2; + ref = ref2; + goto _search2; + } + +_search3: + /* + * Currently we have : + * ml2 > ml1, and + * ip1+3 <= ip2 (usually < ip1+ml1) + */ + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) + new_ml = OPTIMAL_ML; + if (ip + new_ml > start2 + ml2 - MINMATCH) + new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + /* + * Now, we have start2 = ip+new_ml, + * with new_ml=min(ml, OPTIMAL_ML=18) + */ + if (start2 + ml2 < mflimit) + ml3 = lz4hc_insertandgetwidermatch(ctx, + start2 + ml2 - 3, start2, matchlimit, + ml2, &ref3, &start3); + else + ml3 = ml2; + + /* No better match : 2 sequences to encode */ + if (ml3 == ml2) { + /* ip & ref are known; Now for ml */ + if (start2 < ip+ml) + ml = (int)(start2 - ip); + + /* Now, encode 2 sequences */ + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + ip = start2; + lz4_encodesequence(&ip, &op, &anchor, ml2, ref2); + continue; + } + + /* Not enough space for match 2 : remove it */ + if (start3 < ip + ml + 3) { + /* + * can write Seq1 immediately ==> Seq2 is removed, + * so Seq3 becomes Seq1 + */ + if (start3 >= (ip + ml)) { + if (start2 < ip + ml) { + int correction = + (int)(ip + ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _search3; + } + + /* + * OK, now we have 3 ascending matches; let's write at least + * the first one ip & ref are known; Now for ml + */ + if (start2 < ip + ml) { + if ((start2 - ip) < (int)ML_MASK) { + int correction; + if (ml > OPTIMAL_ML) + ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) + ml = (int)(start2 - ip) + ml2 + - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } else + ml = (int)(start2 - ip); + } + lz4_encodesequence(&ip, &op, &anchor, ml, ref); + + ip = start2; + ref = ref2; + ml = ml2; + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + + goto _search3; + } + + /* Encode Last Literals */ + lastrun = (int)(iend - anchor); + if (lastrun >= (int)RUN_MASK) { + *op++ = (RUN_MASK << ML_BITS); + lastrun -= RUN_MASK; + for (; lastrun > 254 ; lastrun -= 255) + *op++ = 255; + *op++ = (u8) lastrun; + } else + *op++ = (lastrun << ML_BITS); + memcpy(op, anchor, iend - anchor); + op += iend - anchor; + /* End */ + return (int) (((char *)op) - dest); +} + +int lz4hc_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem) +{ + int ret = -1; + int out_len = 0; + + struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem; + lz4hc_init(hc4, (const u8 *)src); + out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src, + (char *)dst, (int)src_len); + + if (out_len < 0) + goto exit; + + *dst_len = out_len; + return 0; + +exit: + return ret; +} +EXPORT_SYMBOL_GPL(lz4hc_compress); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZ4HC compressor"); From 2cba2332dd617469248819dc7904d5ea53a5bf18 Mon Sep 17 00:00:00 2001 From: Richard Laager Date: Thu, 22 Aug 2013 16:35:47 -0700 Subject: [PATCH 077/104] lib/lz4: correct the LZ4 license The LZ4 code is listed as using the "BSD 2-Clause License". Signed-off-by: Richard Laager Acked-by: Kyungsik Lee Cc: Chanho Min Cc: Richard Yao Signed-off-by: Andrew Morton [ The 2-clause BSD can be just converted into GPL, but that's rude and pointless, so don't do it - Linus ] Signed-off-by: Linus Torvalds (cherry picked from commit acea9a08809ac718b4e04063f6872067fcc2dbe6) --- lib/lz4/lz4_compress.c | 4 ++-- lib/lz4/lz4_decompress.c | 6 +++--- lib/lz4/lz4hc_compress.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c index fd94058bd7f9..28321d8f75ef 100644 --- a/lib/lz4/lz4_compress.c +++ b/lib/lz4/lz4_compress.c @@ -437,7 +437,7 @@ int lz4_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4_compress); +EXPORT_SYMBOL(lz4_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 compressor"); diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index d3414eae73a1..411be80ddb46 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -299,7 +299,7 @@ int lz4_decompress(const char *src, size_t *src_len, char *dest, return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress); +EXPORT_SYMBOL(lz4_decompress); #endif int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, @@ -319,8 +319,8 @@ int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, return ret; } #ifndef STATIC -EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize); +EXPORT_SYMBOL(lz4_decompress_unknownoutputsize); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4 Decompressor"); #endif diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c index eb1a74f5e368..f344f76b6559 100644 --- a/lib/lz4/lz4hc_compress.c +++ b/lib/lz4/lz4hc_compress.c @@ -533,7 +533,7 @@ int lz4hc_compress(const unsigned char *src, size_t src_len, exit: return ret; } -EXPORT_SYMBOL_GPL(lz4hc_compress); +EXPORT_SYMBOL(lz4hc_compress); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("LZ4HC compressor"); From 9ea7b49a533eff61d632365162d6833f022b5e43 Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Wed, 11 Sep 2013 14:26:32 -0700 Subject: [PATCH 078/104] lz4: fix compression/decompression signedness mismatch LZ4 compression and decompression functions require different in signedness input/output parameters: unsigned char for compression and signed char for decompression. Change decompression API to require "(const) unsigned char *". Signed-off-by: Sergey Senozhatsky Cc: Kyungsik Lee Cc: Geert Uytterhoeven Cc: Yann Collet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds (cherry picked from commit 6d695cba0642a8c843dd1f9edc47accec5bb5a95) --- include/linux/lz4.h | 8 ++++---- lib/lz4/lz4_decompress.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/lz4.h b/include/linux/lz4.h index d21c13f10a64..4356686b0a39 100644 --- a/include/linux/lz4.h +++ b/include/linux/lz4.h @@ -67,8 +67,8 @@ int lz4hc_compress(const unsigned char *src, size_t src_len, * note : Destination buffer must be already allocated. * slightly faster than lz4_decompress_unknownoutputsize() */ -int lz4_decompress(const char *src, size_t *src_len, char *dest, - size_t actual_dest_len); +int lz4_decompress(const unsigned char *src, size_t *src_len, + unsigned char *dest, size_t actual_dest_len); /* * lz4_decompress_unknownoutputsize() @@ -82,6 +82,6 @@ int lz4_decompress(const char *src, size_t *src_len, char *dest, * Error if return (< 0) * note : Destination buffer must be already allocated. */ -int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, - char *dest, size_t *dest_len); +int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len, + unsigned char *dest, size_t *dest_len); #endif diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index 411be80ddb46..df6839e3ce08 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -283,8 +283,8 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, return (int) (-(((char *) ip) - source)); } -int lz4_decompress(const char *src, size_t *src_len, char *dest, - size_t actual_dest_len) +int lz4_decompress(const unsigned char *src, size_t *src_len, + unsigned char *dest, size_t actual_dest_len) { int ret = -1; int input_len = 0; @@ -302,8 +302,8 @@ int lz4_decompress(const char *src, size_t *src_len, char *dest, EXPORT_SYMBOL(lz4_decompress); #endif -int lz4_decompress_unknownoutputsize(const char *src, size_t src_len, - char *dest, size_t *dest_len) +int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len, + unsigned char *dest, size_t *dest_len) { int ret = -1; int out_len = 0; From cb88bcda39dfc409af373ed1e87da180cc9b161d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 20 Jun 2014 22:01:41 -0700 Subject: [PATCH 079/104] lz4: ensure length does not wrap Given some pathologically compressed data, lz4 could possibly decide to wrap a few internal variables, causing unknown things to happen. Catch this before the wrapping happens and abort the decompression. Reported-by: "Don A. Bailey" Cc: stable Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 8664dea287c77a320eeaf42ace763e40b45ba449) --- lib/lz4/lz4_decompress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index df6839e3ce08..99a03acb7d47 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -72,6 +72,8 @@ static int lz4_uncompress(const char *source, char *dest, int osize) len = *ip++; for (; len == 255; length += 255) len = *ip++; + if (unlikely(length > (size_t)(length + len))) + goto _output_error; length += len; } From 223fcd83ae3b2b49367bfd5caf09aaa0ad0dc11a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 24 Jun 2014 16:59:01 -0400 Subject: [PATCH 080/104] lz4: fix another possible overrun There is one other possible overrun in the lz4 code as implemented by Linux at this point in time (which differs from the upstream lz4 codebase, but will get synced at in a future kernel release.) As pointed out by Don, we also need to check the overflow in the data itself. While we are at it, replace the odd error return value with just a "simple" -1 value as the return value is never used for anything other than a basic "did this work or not" check. Reported-by: "Don A. Bailey" Reported-by: Willy Tarreau Cc: stable Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 82fce7033b481e85fc1a9489d0b3a295a2afd446) --- lib/lz4/lz4_decompress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index 99a03acb7d47..b74da447e81e 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -108,6 +108,8 @@ static int lz4_uncompress(const char *source, char *dest, int osize) if (length == ML_MASK) { for (; *ip == 255; length += 255) ip++; + if (unlikely(length > (size_t)(length + *ip))) + goto _output_error; length += *ip++; } @@ -157,7 +159,7 @@ static int lz4_uncompress(const char *source, char *dest, int osize) /* write overflow error detected */ _output_error: - return (int) (-(((char *)ip) - source)); + return -1; } static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, From fd56d7d6af25c7349217f4bee76265d663262783 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 3 Jul 2014 16:06:57 -0700 Subject: [PATCH 081/104] lz4: add overrun checks to lz4_uncompress_unknownoutputsize() Jan points out that I forgot to make the needed fixes to the lz4_uncompress_unknownoutputsize() function to mirror the changes done in lz4_decompress() with regards to potential pointer overflows. The only in-kernel user of this function is the zram code, which only takes data from a valid compressed buffer that it made itself, so it's not a big issue. But due to external kernel modules using this function, it's better to be safe here. Reported-by: Jan Beulich Cc: "Don A. Bailey" Cc: stable Signed-off-by: Greg Kroah-Hartman (cherry picked from commit d0c5ea7736728fbd7302902eafdcddfde286c7bf) --- lib/lz4/lz4_decompress.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c index b74da447e81e..7a85967060a5 100644 --- a/lib/lz4/lz4_decompress.c +++ b/lib/lz4/lz4_decompress.c @@ -192,6 +192,8 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, int s = 255; while ((ip < iend) && (s == 255)) { s = *ip++; + if (unlikely(length > (size_t)(length + s))) + goto _output_error; length += s; } } @@ -232,6 +234,8 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, if (length == ML_MASK) { while (ip < iend) { int s = *ip++; + if (unlikely(length > (size_t)(length + s))) + goto _output_error; length += s; if (s == 255) continue; @@ -284,7 +288,7 @@ static int lz4_uncompress_unknownoutputsize(const char *source, char *dest, /* write overflow error detected */ _output_error: - return (int) (-(((char *) ip) - source)); + return -1; } int lz4_decompress(const unsigned char *src, size_t *src_len, From f300dddc3ef487738f95ee17b20d064c9e91a265 Mon Sep 17 00:00:00 2001 From: Chris Fries Date: Thu, 4 Dec 2014 10:55:40 -0600 Subject: [PATCH 082/104] zram: Add LZ4 support Add LZ4 compressor support. Enable it with CONFIG_ZRAM_LZ4_COMPRESS=y Change-Id: I4bb62c1b51be9171f03386a2b714c6f7542cf321 Signed-off-by: Chris Fries Reviewed-on: http://gerrit.mot.com/694989 Tested-by: Jira Key Reviewed-by: Russell Knize Submit-Approved: Jira Key (cherry picked from commit 1f81bd989451c95ee2461097056f3aee319f704d) --- drivers/staging/zram/Kconfig | 13 ++++++++++++ drivers/staging/zram/zram_drv.c | 36 ++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index 983314c41349..a5eacd618a9c 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -16,6 +16,19 @@ config ZRAM See zram.txt for more information. Project home: +config ZRAM_LZ4_COMPRESS + bool "Enable LZ4 algorithm support" + depends on ZRAM + select LZ4_COMPRESS + select LZ4_DECOMPRESS + default n + help + This option enables LZ4 compression algorithm support, instead + of the default LZO. LZ4 may be faster on some architectures + compared to LZO compression. + + See https://code.google.com/p/lz4/ for more information about LZ4. + config ZRAM_DEBUG bool "Compressed RAM block device debug support" depends on ZRAM diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 83fa65773a5c..a98fb3318ee9 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -30,12 +30,42 @@ #include #include #include +#include #include #include #include #include "zram_drv.h" +static inline int z_decompress_safe(const unsigned char *src, size_t src_len, + unsigned char *dest, size_t *dest_len) +{ +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + return lz4_decompress_unknownoutputsize(src, src_len, dest, dest_len); +#else + return lzo1x_decompress_safe(src, src_len, dest, dest_len); +#endif +} + +static inline int z_compress(const unsigned char *src, size_t src_len, + unsigned char *dst, size_t *dst_len, void *wrkmem) +{ +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + return lz4_compress(src, src_len, dst, dst_len, wrkmem); +#else + return lzo1x_1_compress(src, src_len, dst, dst_len, wrkmem); +#endif +} + +static inline size_t z_scratch_size(void) +{ +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + return LZ4_MEM_COMPRESS; +#else + return LZO1X_MEM_COMPRESS; +#endif +} + /* Globals */ static int zram_major; static struct zram *zram_devices; @@ -210,7 +240,7 @@ static struct zram_meta *zram_meta_alloc(u64 disksize) if (!meta) goto out; - meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + meta->compress_workmem = kzalloc(z_scratch_size(), GFP_KERNEL); if (!meta->compress_workmem) goto free_meta; @@ -337,7 +367,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) if (meta->table[index].size == PAGE_SIZE) copy_page(mem, cmem); else - ret = lzo1x_decompress_safe(cmem, meta->table[index].size, + ret = z_decompress_safe(cmem, meta->table[index].size, mem, &clen); zs_unmap_object(meta->mem_pool, handle); @@ -457,7 +487,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, zram_test_flag(meta, index, ZRAM_ZERO))) zram_free_page(zram, index); - ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, + ret = z_compress(uncmem, PAGE_SIZE, src, &clen, meta->compress_workmem); if (!is_partial_io(bvec)) { From 630f46fcf89796492f901373ca7bcac622118d7f Mon Sep 17 00:00:00 2001 From: Shashank Shekhar Date: Fri, 12 Dec 2014 13:31:13 -0600 Subject: [PATCH 083/104] lib: lz4: Set ARM_EFFICIENT_UNALIGNED_ACCESS Set ARM_EFFICIENT_UNALIGNED_ACCESS to improve performance in lz4 compression and decompression. On msm8x26 cortex-a7, LZO LZ4 LZ4 w/ UA decompress (bs=4k) 121.21 115.52 148.7 LZO LZ4 LZ4 w/ UA compress (bs=4k) 37.5 34.5 44.8 Change-Id: I10dfea380f7558e29576d65f91c8cee13bf8e166 Signed-off-by: Chris Fries Reviewed-on: http://gerrit.mot.com/697567 Tested-by: Jira Key Reviewed-by: Shashank Shekhar Reviewed-by: Igor Kovalenko Submit-Approved: Jira Key (cherry picked from commit 0fbb0d508f7904f0741a174de83f7aa2a65fa1a0) --- lib/lz4/lz4defs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h index abcecdc2d0f2..9b4182fd324c 100644 --- a/lib/lz4/lz4defs.h +++ b/lib/lz4/lz4defs.h @@ -21,6 +21,7 @@ /* * Architecture-specific macros */ +#define ARM_EFFICIENT_UNALIGNED_ACCESS #define BYTE u8 typedef struct _U16_S { u16 v; } U16_S; typedef struct _U32_S { u32 v; } U32_S; From f414b4089f421784f97dad9ee03fb8b66d403de7 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 6 Jan 2014 00:57:54 +0100 Subject: [PATCH 084/104] netfilter: nf_conntrack_dccp: fix skb_header_pointer API usages Some occurences in the netfilter tree use skb_header_pointer() in the following way ... struct dccp_hdr _dh, *dh; ... skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); ... where dh itself is a pointer that is being passed as the copy buffer. Instead, we need to use &_dh as the forth argument so that we're copying the data into an actual buffer that sits on the stack. Currently, we probably could overwrite memory on the stack (e.g. with a possibly mal-formed DCCP packet), but unintentionally, as we only want the buffer to be placed into _dh variable. Change-Id: Ib8ff9f1f0afd905aa31cef49ff8a60344a4624c6 Fixes: 2bc780499aa3 ("[NETFILTER]: nf_conntrack: add DCCP protocol support") Signed-off-by: Daniel Borkmann Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_dccp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 24fdce256cb0..31dbf0050768 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -431,7 +431,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, const char *msg; u_int8_t state; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); BUG_ON(dh == NULL); state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE]; @@ -488,7 +488,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, u_int8_t type, old_state, new_state; enum ct_dccp_roles role; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); BUG_ON(dh == NULL); type = dh->dccph_type; @@ -579,7 +579,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl, unsigned int cscov; const char *msg; - dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &dh); + dh = skb_header_pointer(skb, dataoff, sizeof(_dh), &_dh); if (dh == NULL) { msg = "nf_ct_dccp: short packet "; goto out_invalid; From e6803d53eb9c69f7366a53141bbbcfb593d6d9b1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Jun 2014 12:27:06 +0000 Subject: [PATCH 085/104] futex: Validate atomic acquisition in futex_lock_pi_atomic() We need to protect the atomic acquisition in the kernel against rogue user space which sets the user space futex to 0, so the kernel side acquisition succeeds while there is existing state in the kernel associated to the real owner. Verify whether the futex has waiters associated with kernel state. If it has, return -EINVAL. The state is corrupted already, so no point in cleaning it up. Subsequent calls will fail as well. Not our problem. [ tglx: Use futex_top_waiter() and explain why we do not need to try restoring the already corrupted user space state. ] Change-Id: Idb5735bd185d010401525fcd419870558d7340c6 Signed-off-by: Darren Hart Cc: Kees Cook Cc: Will Drewry Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner Git-commit: 550c7910f0e2fd4f130fec2f17541f3614fdfaf9 Git-repo: https://android.googlesource.com/kernel/common.git Signed-off-by: Ian Maund --- kernel/futex.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 9536918cac29..9ecd6abadb66 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -741,10 +741,18 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, return -EDEADLK; /* - * Surprise - we got the lock. Just return to userspace: + * Surprise - we got the lock, but we do not trust user space at all. */ - if (unlikely(!curval)) - return 1; + if (unlikely(!curval)) { + /* + * We verify whether there is kernel state for this + * futex. If not, we can safely assume, that the 0 -> + * TID transition is correct. If state exists, we do + * not bother to fixup the user space state as it was + * corrupted already. + */ + return futex_top_waiter(hb, key) ? -EINVAL : 1; + } uval = curval; From 154fbf2421093a119096011d30649fa16c2670eb Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Jun 2014 12:27:07 +0000 Subject: [PATCH 086/104] futex: Always cleanup owner tid in unlock_pi If the owner died bit is set at futex_unlock_pi, we currently do not cleanup the user space futex. So the owner TID of the current owner (the unlocker) persists. That's observable inconsistant state, especially when the ownership of the pi state got transferred. Clean it up unconditionally. Change-Id: I9b8da1438c61a7c30a11ad91550f00c0a7349129 Signed-off-by: Thomas Gleixner Cc: Kees Cook Cc: Will Drewry Cc: Darren Hart Cc: stable@vger.kernel.org Git-commit: a2ec8e3dcdc6c93f574a0e22039b791cc5e14fa6 Git-repo: https://android.googlesource.com/kernel/common.git Signed-off-by: Ian Maund --- kernel/futex.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 9ecd6abadb66..5235df88a061 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -876,6 +876,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) struct task_struct *new_owner; struct futex_pi_state *pi_state = this->pi_state; u32 uninitialized_var(curval), newval; + int ret = 0; if (!pi_state) return -EINVAL; @@ -899,23 +900,19 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) new_owner = this->task; /* - * We pass it to the next owner. (The WAITERS bit is always - * kept enabled while there is PI state around. We must also - * preserve the owner died bit.) + * We pass it to the next owner. The WAITERS bit is always + * kept enabled while there is PI state around. We cleanup the + * owner died bit, because we are the owner. */ - if (!(uval & FUTEX_OWNER_DIED)) { - int ret = 0; - - newval = FUTEX_WAITERS | task_pid_vnr(new_owner); + newval = FUTEX_WAITERS | task_pid_vnr(new_owner); - if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) - ret = -EFAULT; - else if (curval != uval) - ret = -EINVAL; - if (ret) { - raw_spin_unlock(&pi_state->pi_mutex.wait_lock); - return ret; - } + if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) + ret = -EFAULT; + else if (curval != uval) + ret = -EINVAL; + if (ret) { + raw_spin_unlock(&pi_state->pi_mutex.wait_lock); + return ret; } raw_spin_lock_irq(&pi_state->owner->pi_lock); @@ -2137,9 +2134,10 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) /* * To avoid races, try to do the TID -> 0 atomic transition * again. If it succeeds then we can return without waking - * anyone else up: + * anyone else up. We only try this if neither the waiters nor + * the owner died bit are set. */ - if (!(uval & FUTEX_OWNER_DIED) && + if (!(uval & ~FUTEX_TID_MASK) && cmpxchg_futex_value_locked(&uval, uaddr, vpid, 0)) goto pi_faulted; /* @@ -2171,11 +2169,9 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) /* * No waiters - kernel unlocks the futex: */ - if (!(uval & FUTEX_OWNER_DIED)) { - ret = unlock_futex_pi(uaddr, uval); - if (ret == -EFAULT) - goto pi_faulted; - } + ret = unlock_futex_pi(uaddr, uval); + if (ret == -EFAULT) + goto pi_faulted; out_unlock: spin_unlock(&hb->lock); From 98e3035fe8ba2e3527e39ed758eb29c91f408b08 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 3 Jun 2014 12:27:08 +0000 Subject: [PATCH 087/104] futex: Make lookup_pi_state more robust The current implementation of lookup_pi_state has ambigous handling of the TID value 0 in the user space futex. We can get into the kernel even if the TID value is 0, because either there is a stale waiters bit or the owner died bit is set or we are called from the requeue_pi path or from user space just for fun. The current code avoids an explicit sanity check for pid = 0 in case that kernel internal state (waiters) are found for the user space address. This can lead to state leakage and worse under some circumstances. Handle the cases explicit: Waiter | pi_state | pi->owner | uTID | uODIED | ? [1] NULL | --- | --- | 0 | 0/1 | Valid [2] NULL | --- | --- | >0 | 0/1 | Valid [3] Found | NULL | -- | Any | 0/1 | Invalid [4] Found | Found | NULL | 0 | 1 | Valid [5] Found | Found | NULL | >0 | 1 | Invalid [6] Found | Found | task | 0 | 1 | Valid [7] Found | Found | NULL | Any | 0 | Invalid [8] Found | Found | task | ==taskTID | 0/1 | Valid [9] Found | Found | task | 0 | 0 | Invalid [10] Found | Found | task | !=taskTID | 0/1 | Invalid [1] Indicates that the kernel can acquire the futex atomically. We came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit. [2] Valid, if TID does not belong to a kernel thread. If no matching thread is found then it indicates that the owner TID has died. [3] Invalid. The waiter is queued on a non PI futex [4] Valid state after exit_robust_list(), which sets the user space value to FUTEX_WAITERS | FUTEX_OWNER_DIED. [5] The user space value got manipulated between exit_robust_list() and exit_pi_state_list() [6] Valid state after exit_pi_state_list() which sets the new owner in the pi_state but cannot access the user space value. [7] pi_state->owner can only be NULL when the OWNER_DIED bit is set. [8] Owner and user space value match [9] There is no transient state which sets the user space TID to 0 except exit_robust_list(), but this is indicated by the FUTEX_OWNER_DIED bit. See [4] [10] There is no transient state which leaves owner and user space TID out of sync. Backport to 3.13 conflicts: kernel/futex.c Change-Id: Ic15b8229880892a36ebbdf6c693ffba26ea03b3b Signed-off-by: Thomas Gleixner Signed-off-by: John Johansen Cc: Kees Cook Cc: Will Drewry Cc: Darren Hart Cc: stable@vger.kernel.org Git-commit: b50939f89dae691f8da9e0fd22e0eae0db1c0ae4 Git-repo: https://android.googlesource.com/kernel/common.git Signed-off-by: Ian Maund --- kernel/futex.c | 123 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 17 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 5235df88a061..0ef42cfab5e9 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -587,6 +587,55 @@ void exit_pi_state_list(struct task_struct *curr) raw_spin_unlock_irq(&curr->pi_lock); } +/* + * We need to check the following states: + * + * Waiter | pi_state | pi->owner | uTID | uODIED | ? + * + * [1] NULL | --- | --- | 0 | 0/1 | Valid + * [2] NULL | --- | --- | >0 | 0/1 | Valid + * + * [3] Found | NULL | -- | Any | 0/1 | Invalid + * + * [4] Found | Found | NULL | 0 | 1 | Valid + * [5] Found | Found | NULL | >0 | 1 | Invalid + * + * [6] Found | Found | task | 0 | 1 | Valid + * + * [7] Found | Found | NULL | Any | 0 | Invalid + * + * [8] Found | Found | task | ==taskTID | 0/1 | Valid + * [9] Found | Found | task | 0 | 0 | Invalid + * [10] Found | Found | task | !=taskTID | 0/1 | Invalid + * + * [1] Indicates that the kernel can acquire the futex atomically. We + * came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit. + * + * [2] Valid, if TID does not belong to a kernel thread. If no matching + * thread is found then it indicates that the owner TID has died. + * + * [3] Invalid. The waiter is queued on a non PI futex + * + * [4] Valid state after exit_robust_list(), which sets the user space + * value to FUTEX_WAITERS | FUTEX_OWNER_DIED. + * + * [5] The user space value got manipulated between exit_robust_list() + * and exit_pi_state_list() + * + * [6] Valid state after exit_pi_state_list() which sets the new owner in + * the pi_state but cannot access the user space value. + * + * [7] pi_state->owner can only be NULL when the OWNER_DIED bit is set. + * + * [8] Owner and user space value match + * + * [9] There is no transient state which sets the user space TID to 0 + * except exit_robust_list(), but this is indicated by the + * FUTEX_OWNER_DIED bit. See [4] + * + * [10] There is no transient state which leaves owner and user space + * TID out of sync. + */ static int lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, union futex_key *key, struct futex_pi_state **ps) @@ -602,12 +651,13 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, plist_for_each_entry_safe(this, next, head, list) { if (match_futex(&this->key, key)) { /* - * Another waiter already exists - bump up - * the refcount and return its pi_state: + * Sanity check the waiter before increasing + * the refcount and attaching to it. */ pi_state = this->pi_state; /* - * Userspace might have messed up non-PI and PI futexes + * Userspace might have messed up non-PI and + * PI futexes [3] */ if (unlikely(!pi_state)) return -EINVAL; @@ -615,34 +665,70 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, WARN_ON(!atomic_read(&pi_state->refcount)); /* - * When pi_state->owner is NULL then the owner died - * and another waiter is on the fly. pi_state->owner - * is fixed up by the task which acquires - * pi_state->rt_mutex. - * - * We do not check for pid == 0 which can happen when - * the owner died and robust_list_exit() cleared the - * TID. + * Handle the owner died case: */ - if (pid && pi_state->owner) { + if (uval & FUTEX_OWNER_DIED) { + /* + * exit_pi_state_list sets owner to NULL and + * wakes the topmost waiter. The task which + * acquires the pi_state->rt_mutex will fixup + * owner. + */ + if (!pi_state->owner) { + /* + * No pi state owner, but the user + * space TID is not 0. Inconsistent + * state. [5] + */ + if (pid) + return -EINVAL; + /* + * Take a ref on the state and + * return. [4] + */ + goto out_state; + } + + /* + * If TID is 0, then either the dying owner + * has not yet executed exit_pi_state_list() + * or some waiter acquired the rtmutex in the + * pi state, but did not yet fixup the TID in + * user space. + * + * Take a ref on the state and return. [6] + */ + if (!pid) + goto out_state; + } else { /* - * Bail out if user space manipulated the - * futex value. + * If the owner died bit is not set, + * then the pi_state must have an + * owner. [7] */ - if (pid != task_pid_vnr(pi_state->owner)) + if (!pi_state->owner) return -EINVAL; } + /* + * Bail out if user space manipulated the + * futex value. If pi state exists then the + * owner TID must be the same as the user + * space TID. [9/10] + */ + if (pid != task_pid_vnr(pi_state->owner)) + return -EINVAL; + + out_state: atomic_inc(&pi_state->refcount); *ps = pi_state; - return 0; } } /* * We are the first waiter - try to look up the real owner and attach - * the new pi_state to it, but bail out when TID = 0 + * the new pi_state to it, but bail out when TID = 0 [1] */ if (!pid) return -ESRCH; @@ -670,6 +756,9 @@ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, return ret; } + /* + * No existing pi state. First waiter. [2] + */ pi_state = alloc_pi_state(); /* From cbcc180c81f5f1456ed9d8e257a9f53c8e4c62df Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 16 Aug 2012 18:14:14 +0100 Subject: [PATCH 088/104] tracing/syscalls: Fix perf syscall tracing when syscall_nr == -1 commit 60916a9382e88fbf5e54fd36a3e658efd7ab7bed upstream. syscall_get_nr can return -1 in the case that the task is not executing a system call. This patch fixes perf_syscall_{enter,exit} to check that the syscall number is valid before using it as an index into a bitmap. Link: http://lkml.kernel.org/r/1345137254-7377-1-git-send-email-will.deacon@arm.com Change-Id: I010666b85491f1f282c9a7d56a3a23dd9a50e16a Cc: Jason Baron Cc: Wade Farnsworth Cc: Frederic Weisbecker Signed-off-by: Will Deacon Signed-off-by: Steven Rostedt Signed-off-by: Zefan Li --- kernel/trace/trace_syscalls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 96fc73369099..4bc7d92dbdb2 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -506,6 +506,8 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) int size; syscall_nr = syscall_get_nr(current, regs); + if (syscall_nr < 0) + return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; @@ -580,6 +582,8 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int size; syscall_nr = syscall_get_nr(current, regs); + if (syscall_nr < 0) + return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; From 146dcea6c66c1b0877fbf5df03a4967ba675fe15 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 29 Oct 2014 23:06:58 +0100 Subject: [PATCH 089/104] tracing/syscalls: Ignore numbers outside NR_syscalls' range commit 086ba77a6db00ed858ff07451bedee197df868c9 upstream. ARM has some private syscalls (for example, set_tls(2)) which lie outside the range of NR_syscalls. If any of these are called while syscall tracing is being performed, out-of-bounds array access will occur in the ftrace and perf sys_{enter,exit} handlers. # trace-cmd record -e raw_syscalls:* true && trace-cmd report ... true-653 [000] 384.675777: sys_enter: NR 192 (0, 1000, 3, 4000022, ffffffff, 0) true-653 [000] 384.675812: sys_exit: NR 192 = 1995915264 true-653 [000] 384.675971: sys_enter: NR 983045 (76f74480, 76f74000, 76f74b28, 76f74480, 76f76f74, 1) true-653 [000] 384.675988: sys_exit: NR 983045 = 0 ... # trace-cmd record -e syscalls:* true [ 17.289329] Unable to handle kernel paging request at virtual address aaaaaace [ 17.289590] pgd = 9e71c000 [ 17.289696] [aaaaaace] *pgd=00000000 [ 17.289985] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 17.290169] Modules linked in: [ 17.290391] CPU: 0 PID: 704 Comm: true Not tainted 3.18.0-rc2+ #21 [ 17.290585] task: 9f4dab00 ti: 9e710000 task.ti: 9e710000 [ 17.290747] PC is at ftrace_syscall_enter+0x48/0x1f8 [ 17.290866] LR is at syscall_trace_enter+0x124/0x184 Fix this by ignoring out-of-NR_syscalls-bounds syscall numbers. Commit cd0980fc8add "tracing: Check invalid syscall nr while tracing syscalls" added the check for less than zero, but it should have also checked for greater than NR_syscalls. Link: http://lkml.kernel.org/p/1414620418-29472-1-git-send-email-rabin@rab.in Change-Id: I7107c6baae1983e17ac20d97cd3502e9a3867196 Fixes: cd0980fc8add "tracing: Check invalid syscall nr while tracing syscalls" Signed-off-by: Rabin Vincent Signed-off-by: Steven Rostedt [lizf: Backported to 3.4: adjust context] Signed-off-by: Zefan Li --- kernel/trace/trace_syscalls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 4bc7d92dbdb2..277ee7f84072 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -307,7 +307,7 @@ void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id) int syscall_nr; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_enter_syscalls)) return; @@ -341,7 +341,7 @@ void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int syscall_nr; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_exit_syscalls)) return; @@ -506,7 +506,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) int size; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) return; @@ -582,7 +582,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int size; syscall_nr = syscall_get_nr(current, regs); - if (syscall_nr < 0) + if (syscall_nr < 0 || syscall_nr >= NR_syscalls) return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) return; From b3be54e37d688e2ae6512d2c4638a744f4a2d30b Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 29 Dec 2014 09:39:01 -0500 Subject: [PATCH 090/104] KEYS: close race between key lookup and freeing commit a3a8784454692dd72e5d5d34dcdab17b4420e74c upstream. When a key is being garbage collected, it's key->user would get put before the ->destroy() callback is called, where the key is removed from it's respective tracking structures. This leaves a key hanging in a semi-invalid state which leaves a window open for a different task to try an access key->user. An example is find_keyring_by_name() which would dereference key->user for a key that is in the process of being garbage collected (where key->user was freed but ->destroy() wasn't called yet - so it's still present in the linked list). This would cause either a panic, or corrupt memory. Fixes CVE-2014-9529. Change-Id: I22bee631f32f9296f9db612793150fa05e2534b4 Signed-off-by: Sasha Levin Signed-off-by: David Howells Signed-off-by: Greg Kroah-Hartman --- security/keys/gc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/keys/gc.c b/security/keys/gc.c index a42b45531aac..87632bd17b3e 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -188,12 +188,12 @@ static noinline void key_gc_unused_key(struct key *key) if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) atomic_dec(&key->user->nikeys); - key_user_put(key->user); - /* now throw away the key memory */ if (key->type->destroy) key->type->destroy(key); + key_user_put(key->user); + kfree(key->description); #ifdef KEY_DEBUGGING From 67526734bbcdf1f8a40c2c4ce818ee7005773048 Mon Sep 17 00:00:00 2001 From: vm03 Date: Sun, 10 May 2015 13:04:46 +0300 Subject: [PATCH 091/104] dt: fix camera fo ms323 Change-Id: I153d5f65ab1db689309b757b400678667ddf3a79 --- .../boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi index 0986ea0c39d9..c427ceabcad3 100644 --- a/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi +++ b/arch/arm/boot/dts/msm8610-w5n_global_com/msm8610-w5n-camera.dtsi @@ -91,6 +91,7 @@ cell-index = <0>; //Use Af_main_0 of dw9716 with HI543(5M) reg = <0x1A 0x0>; compatible = "qcom,actuator"; + status = "okay"; }; /* IMX111(8M) eeprom */ @@ -280,7 +281,7 @@ qcom,csi-lane-mask = <0x7>; qcom,sensor-position = <0>; qcom,sensor-mode = <0>; - status = "decide_in_lk"; + status = "okay"; revision = "rev_b..."; }; From c5d01f59fe09f565a3516bc63c9931b1085aef15 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 30 May 2015 09:16:53 -0700 Subject: [PATCH 092/104] udp: fix behavior of wrong checksums We have two problems in UDP stack related to bogus checksums : 1) We return -EAGAIN to application even if receive queue is not empty. This breaks applications using edge trigger epoll() 2) Under UDP flood, we can loop forever without yielding to other processes, potentially hanging the host, especially on non SMP. This patch is an attempt to make things better. We might in the future add extra support for rt applications wanting to better control time spent doing a recv() in a hostile environment. For example we could validate checksums before queuing packets in socket receive queue. Change-Id: I2757c483bd7b090ac8dd18178b5d0733a6711e1f Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Signed-off-by: David S. Miller --- net/ipv4/udp.c | 6 ++---- net/ipv6/udp.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 4d844344f262..18c43ad0620f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1253,10 +1253,8 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_INERRORS, is_udplite); unlock_sock_fast(sk, slow); - if (noblock) - return -EAGAIN; - - /* starting over for a new packet */ + /* starting over for a new packet, but check if we need to yield */ + cond_resched(); msg->msg_flags &= ~MSG_TRUNC; goto try_again; } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a20d55dc9c2a..5fd41f9f8bd7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -454,10 +454,8 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, } unlock_sock_fast(sk, slow); - if (noblock) - return -EAGAIN; - - /* starting over for a new packet */ + /* starting over for a new packet, but check if we need to yield */ + cond_resched(); msg->msg_flags &= ~MSG_TRUNC; goto try_again; } From 31492cbf55bfa37f148e7c427e30a1f55dfcb646 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Sun, 19 Apr 2015 02:48:39 +0200 Subject: [PATCH 093/104] fs: take i_mutex during prepare_binprm for set[ug]id executables This prevents a race between chown() and execve(), where chowning a setuid-user binary to root would momentarily make the binary setuid root. This patch was mostly written by Linus Torvalds. Signed-off-by: Jann Horn Signed-off-by: Linus Torvalds Conflicts: fs/exec.c Change-Id: Iecebf23d07e299689e4ba4fd74ea8821ef96e72b --- fs/exec.c | 65 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index b1fd2025e59a..56532f10959c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1268,6 +1268,45 @@ static int check_unsafe_exec(struct linux_binprm *bprm) return res; } +static void bprm_fill_uid(struct linux_binprm *bprm) +{ + struct inode *inode; + unsigned int mode; + uid_t uid; + gid_t gid; + + /* clear any previous set[ug]id data from a previous binary */ + bprm->cred->euid = current_euid(); + bprm->cred->egid = current_egid(); + + if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + return; + + inode = bprm->file->f_path.dentry->d_inode; + mode = ACCESS_ONCE(inode->i_mode); + if (!(mode & (S_ISUID|S_ISGID))) + return; + + /* Be careful if suid/sgid is set */ + mutex_lock(&inode->i_mutex); + + /* reload atomically mode/uid/gid now that lock held */ + mode = inode->i_mode; + uid = inode->i_uid; + gid = inode->i_gid; + mutex_unlock(&inode->i_mutex); + + if (mode & S_ISUID) { + bprm->per_clear |= PER_CLEAR_ON_SETID; + bprm->cred->euid = uid; + } + + if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { + bprm->per_clear |= PER_CLEAR_ON_SETID; + bprm->cred->egid = gid; + } +} + /* * Fill the binprm structure from the inode. * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes @@ -1276,36 +1315,12 @@ static int check_unsafe_exec(struct linux_binprm *bprm) */ int prepare_binprm(struct linux_binprm *bprm) { - umode_t mode; - struct inode * inode = bprm->file->f_path.dentry->d_inode; int retval; - mode = inode->i_mode; if (bprm->file->f_op == NULL) return -EACCES; - /* clear any previous set[ug]id data from a previous binary */ - bprm->cred->euid = current_euid(); - bprm->cred->egid = current_egid(); - - if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) { - /* Set-uid? */ - if (mode & S_ISUID) { - bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->euid = inode->i_uid; - } - - /* Set-gid? */ - /* - * If setgid is set but no group execute bit then this - * is a candidate for mandatory locking, not a setgid - * executable. - */ - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->per_clear |= PER_CLEAR_ON_SETID; - bprm->cred->egid = inode->i_gid; - } - } + bprm_fill_uid(bprm); /* fill in binprm security blob */ retval = security_bprm_set_creds(bprm); From aa64a82324866be7621439ad8eed0a427c28be29 Mon Sep 17 00:00:00 2001 From: vm03 Date: Wed, 12 Aug 2015 16:13:55 +0300 Subject: [PATCH 094/104] pm: add LGE battery code Change-Id: If7d2382c7920015c11a778d85c647688908f405d --- .../include/mach/lge_charging_scenario.h | 110 ++ drivers/power/Kconfig | 185 ++ drivers/power/power_supply_core.c | 47 + drivers/power/power_supply_sysfs.c | 212 ++ drivers/power/qpnp-bms.c | 472 ++++- drivers/power/qpnp-charger.c | 1726 ++++++++++++++++- drivers/usb/otg/msm_otg.c | 377 +++- include/linux/batterydata-lib.h | 37 + include/linux/power_supply.h | 19 + include/linux/usb/msm_hsusb.h | 11 +- 10 files changed, 3081 insertions(+), 115 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/lge_charging_scenario.h diff --git a/arch/arm/mach-msm/include/mach/lge_charging_scenario.h b/arch/arm/mach-msm/include/mach/lge_charging_scenario.h new file mode 100644 index 000000000000..759bc0352c9f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/lge_charging_scenario.h @@ -0,0 +1,110 @@ +/* + * LGE charging scenario Header file. + * + * Copyright (C) 2013 LG Electronics + * mansu.lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LGE_CHARGING_SCENARIO_H_ +#define __LGE_CHARGING_SCENARIO_H_ + +#include + +#ifndef CONFIG_LGE_PM_THERMAL +#define CONFIG_LGE_PM_THERMAL +#endif + +#define DC_IUSB_VOLTUV 4000000 +#ifdef CONFIG_LGE_PM_VZW_CHARGING_TEMP_SCENARIO +#define DC_IUSB_CURRENT 400 +#else +#define DC_IUSB_CURRENT 450 +#endif +#define DC_CURRENT_DEF -1 + +/* Battery temperature states */ +enum lge_battemp_states { + CHG_BATTEMP_BL_M11, + CHG_BATTEMP_M10_M5, + CHG_BATTEMP_M4_41, + CHG_BATTEMP_42_45, +#ifdef CONFIG_LGE_PM_VZW_CHARGING_TEMP_SCENARIO + CHG_BATTEMP_46_52, + CHG_BATTEMP_53_OT, +#else + CHG_BATTEMP_46_OT, +#endif + CHG_BATTEMP_AB_OT, + +}; + +/* */ +enum lge_charging_states { + CHG_BATT_NORMAL_STATE, + CHG_BATT_DECCUR_STATE, + CHG_BATT_WARNIG_STATE, + CHG_BATT_STPCHG_STATE, +}; + +/* */ +enum lge_states_changes { + STS_CHE_NONE, + STS_CHE_NORMAL_TO_DECCUR, + STS_CHE_NORMAL_TO_STPCHG, + STS_CHE_DECCUR_TO_NORAML, + STS_CHE_DECCUR_TO_STPCHG, + STS_CHE_STPCHG_TO_NORMAL, +#ifdef CONFIG_LGE_PM_VZW_CHARGING_TEMP_SCENARIO + STS_CHE_STPCHG_TO_DECCUR +#endif +}; + +/* BTM status */ +enum lge_btm_states { + BTM_HEALTH_GOOD, + BTM_HEALTH_OVERHEAT, + BTM_HEALTH_COLD, +}; + +struct charging_info { + int batt_volt; + int batt_temp; + int is_charger; +#ifdef CONFIG_LGE_PM_THERMAL + int chg_current_ma; + int chg_current_te; +#endif + bool is_charger_changed; +}; + +struct charging_rsp { + enum lge_charging_states state; + enum lge_states_changes change_lvl; + bool force_update; + bool disable_chg; + int dc_current; + enum lge_btm_states btm_state; + int pseudo_chg_ui; +}; + +struct batt_temp_table { + int min; + int max; + enum lge_battemp_states battemp_state; +}; + +extern void +lge_monitor_batt_temp(struct charging_info req, struct charging_rsp *res); +#endif +/* */ + diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e42337f75e33..29ad1744e053 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -495,4 +495,189 @@ config QPNP_BMS Say Y here to enable support for QPNP chip bms device. It registers a fuelgauge bms power supply to report State of Charge. + +config LGE_PM + tristate "PM8x26 LGE Feature" + depends on SPMI + help + Say Y here to enable support for pm8941 chip lge feature. + LGE_PM releasted changed. + Power Management, Battery charging, Battery Monitoring System + with battery FET and fuel gauge. + +config MAX17048_FUELGAUGE + tristate "Maxim MAX17048 fuel gauging external driver" + depends on I2C && LGE_PM + default n + help + MAX17048 is fuel-gauge systems for lithium-ion (Li+) batteries + in handheld and portable equipment. The MAX17048 is configured + to operate with a single lithium cell + +config LGE_PM_BATTERY_ID_CHECKER + bool "Virtual Device for LGE Battery ID Checker" + depends on LGE_PM + help + Say Y here to enable LGE Battery ID Checker + +config LGE_PM_BATTERY_4_2VOLT + bool "LGE Support 4.2V Battery" + depends on LGE_PM + help + Say Y here to enable LGE Support 4.2V Battery + +config LGE_PM_FACTORY_PSEUDO_BATTERY + bool "Pseudo battery mode setting for factory test" + depends on LGE_PM + help + Say Y here to enable pseudo mode setting + +config LGE_PM_FACTORY_TESTMODE + bool "testmode node setting for battery testmode" + depends on LGE_PM + help + Say Y here to enable testmode setting + +config LGE_PM_USB_ID + bool "Support LGE USB_ID" + depends on LGE_PM + help + Say Y here to enable USB_ID + +config LGE_PM_CHARGING_CHARGERLOGO + bool "LGE Chargerlogo Feature" + depends on LGE_PM + help + Say Y here to enable LGE charger logo + +config LGE_PM_CHARGING_TEMP_SCENARIO + bool "LGE Charging Temp Scenario" + depends on LGE_PM + help + Say Y here to enable LGE Charging Temp Scenario + +config LGE_PM_VZW_CHARGING_TEMP_SCENARIO + bool "VZW Charging Temp Scenario" + depends on LGE_PM + help + Say Y here to enable LGE Charging Temp Scenario + +config LGE_PM_BATTERY_PROFILE_DATA + bool "Apply battery profile data for W7" + depends on LGE_PM + help + Apply battery profile data for W7. + bms-batterydata-LGE_BL_54SH_2540mAh_LG_Chem_Final.c + bms-batterydata-LGE_BL_54SH_2540mAh_Sanyo_Final.c + +config LGE_PM_CHARGING_EXTERNAL_OVP + bool "LGE Using External OVP" + depends on LGE_PM + help + This config will be for external ovp. + +config LGE_PM_BATTERY_SOC_RESCALING + bool "SOC Rescaling" + depends on LGE_PM + help + Enable SOC Rescaling. + +config LGE_PM_BMS_MIN_IAVG_CAL_TIME + bool "apply BMS_MIN_IAVG_CAL_TIME 20sec" + depends on LGE_PM + help + Enable BMS_MIN_IAVG_CAL_TIME 20. + +config LGE_PM_BMS_VERY_LOW_CAL_TIME + bool "apply LGE_PM_BMS_VERY_LOW_CAL_TIME" + depends on LGE_PM + help + Enable LGE_PM_BMS_VERY_LOW_CAL_TIME. + +config LGE_PM_NEED_TO_MONITORING_QCT_PATCH + bool "LGE internal configuration for QCT patch" + help + This is LGE internal feature for QCT Patch. + Featured functions will be removed after QCT's patch. + +config LGE_PM_4_25V_CHARGING_START + bool "LGE Charging Start at 4.25V" + depends on LGE_PM + help + Charging Start at 4.25V battery. + +config LGE_PM_SMPL_COUNT + bool "SMPL Counter" + depends on LGE_PM + help + Say Y here to enable smpl count for hiddenmenu. + +#LGE_CHANGE_S, mg.jeong@lge.com, 12-07-04, Reason*/ +config LGE_WAKE_IRQ_PRINT + bool "Print IRQ when resuming..." + depends on LGE_PM + help + This enables to print irqs when waking up from suspend.. +#LGE_CHANGE_E, mg.jeong@lge.com, 12-07-04, Reason*/ + +config LGE_PM_THERMAL + bool "Control IUSBMAX current by thermal-engine" + depends on LGE_PM + help + IUSBMAX current could be controlled by Thermal-engine. + +config LGE_PM_VZW_FAST_CHG + bool "Applying VZW requirement" + depends on LGE_PM + help + Say Y here to meet VZW requirement. + +config LGE_PM_VZW_HIGH_TEMP_PWR_OFF + bool "Notify power-off due to high temperature in chargerlogo" + depends on LGE_PM + help + Say Y here to notify in chargerlogo when occurring power-off due to high temperature + +config LGE_PM_VZW_LLK + bool "VZW store mode" + depends on LGE_PM + help + Say Y here to enable VZW LLK mode. + +config LGE_PM_PWR_KEY_FOR_CHG_LOGO + bool "Control the PWR KEY for charger logo" + depends on LGE_PM + help + PWR KEY could be controlled by this code. + +config LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST + bool "Workaround code for USB false detection" + depends on LGE_PM + help + This code is workaround code for loss of UVD detection by reverse boost of PM8110 & PM8x26 + +config LGE_PM_WORKAROUND_PORT_OPEN_FAIL_IN_FACTORY_TEST + bool "Workaround code for port open fail in factory test" + depends on LGE_PM + help + This code is workaround code for OVD detection by USBIN Voltage in factory test + +config LGE_PM_BMS_FACTORY_TEST + bool "Workaround code for fuel gauge fail in factory test" + depends on LGE_PM + help + This code is workaround code for fuel gauge fail in factory test. + +config LGE_USING_CHG_LED + bool "PM8226 LED Indicator is connected CHG_LED" + depends on LGE_PM + help + Using CHG_LED + +config LGE_PM_BMS_CC_ACCURACY_PATCH + bool "Workaround code for cc accuracy problem after EOC with TA" + depends on LGE_PM + help + This code is workaround code for cc accuracy problem after EOC with TA. + endif # POWER_SUPPLY diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 97e952eb8f09..fce9af414069 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -137,11 +137,58 @@ EXPORT_SYMBOL_GPL(power_supply_set_scope); * @psy: the power supply to control * @supply_type: sets type property of power supply */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP +static bool is_chg_detect_first = true; + +bool lge_check_fast_chg_irq(void); +void lge_set_chg_path_to_external(void); +void lge_set_chg_path_to_internal(void); +#endif + +#ifdef CONFIG_LGE_PM +int global_supply_type = 0; + +int lge_power_supply_get_type(void) +{ + return global_supply_type; +} + EXPORT_SYMBOL(lge_power_supply_get_type); + + +void lge_power_supply_set_type(int supply_type) +{ + global_supply_type = supply_type; + + pr_err("======== [lge_power_supply_set_type] SUPPLY_TYPE = %d =========\n", global_supply_type); +} +EXPORT_SYMBOL(lge_power_supply_set_type); +#endif + int power_supply_set_supply_type(struct power_supply *psy, enum power_supply_type supply_type) { const union power_supply_propval ret = {supply_type,}; +#ifdef CONFIG_LGE_PM + global_supply_type = supply_type; +#endif + +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP + if(is_chg_detect_first && lge_check_fast_chg_irq()) + { + if(supply_type == POWER_SUPPLY_TYPE_USB_DCP) + { + //pr_debug("======= [Power Supply Set] DCP CABLE - Ext. OVP is HIGH =============\n"); + lge_set_chg_path_to_external(); + } + else + { + //pr_debug("======= [Power Supply Set] SCP CABLE - Ext. OVP is LOW =============\n"); + lge_set_chg_path_to_internal(); + } + is_chg_detect_first = false; + } +#endif if (psy->set_property) return psy->set_property(psy, POWER_SUPPLY_PROP_TYPE, &ret); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 46885141660b..1eca855bdeec 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -19,6 +19,10 @@ #include "power_supply.h" +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY +#include +#endif + /* * This is because the name "current" breaks the device attr macro. * The "current" word resolves to "(get_current())" so instead of @@ -31,12 +35,48 @@ * (as a macro let's say). */ +/* + */ +#ifdef CONFIG_LGE_PM +#define POWER_SUPPLY_ATTR(_name) \ +{ \ + .attr = { .name = #_name }, \ + .show = power_supply_show_property, \ + .store = power_supply_store_property, \ +} +#else // QCT ORG #define POWER_SUPPLY_ATTR(_name) \ { \ .attr = { .name = #_name }, \ .show = power_supply_show_property, \ .store = power_supply_store_property, \ } +#endif +/* */ + +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY +#define PSEUDO_BATT_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = 0644}, \ + .show = pseudo_batt_show_property, \ + .store = pseudo_batt_store_property, \ +} +#endif + +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +#define DIAG_CHCOMP_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = 0644}, \ + .show = at_chg_complete_show, \ +} + +#define DIAG_CHARGE_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = 0644}, \ + .show = at_chg_status_show, \ + .store = at_chg_status_store, \ +} +#endif static struct device_attribute power_supply_attrs[]; @@ -67,6 +107,21 @@ static ssize_t power_supply_show_property(struct device *dev, static char *scope_text[] = { "Unknown", "System", "Device" }; +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +#if defined(CONFIG_MACH_MSM8926_X3N_OPEN_EU) || defined(CONFIG_MACH_MSM8926_X3N_GLOBAL_COM) || \ + defined(CONFIG_MACH_MSM8926_X3_TRF_US) || defined(CONFIG_MACH_MSM8926_X3_KR) + static char *lge_hw_rev_text[] = { + "rev_0", "rev_a", "rev_a2", "rev_b", "rev_c","rev_d", "rev_10", "rev_11", "revserved" + }; +#else + static char *lge_hw_rev_text[] = { + "rev_0", "rev_a", "rev_b", "rev_c", "rev_d","rev_e", "rev_10", "rev_11", "revserved" + }; +#endif + static char *lge_usb_id_text[] = { + "NOT INIT", "56K", "100K","130K", "180K", "200K", "220K", "270K", "330K", "620K","910K", "OPEN" + }; +#endif ssize_t ret = 0; struct power_supply *psy = dev_get_drvdata(dev); const ptrdiff_t off = attr - power_supply_attrs; @@ -101,6 +156,12 @@ static ssize_t power_supply_show_property(struct device *dev, return sprintf(buf, "%s\n", type_text[value.intval]); else if (off == POWER_SUPPLY_PROP_SCOPE) return sprintf(buf, "%s\n", scope_text[value.intval]); +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + else if (off == POWER_SUPPLY_PROP_HW_REV) + return sprintf(buf, "%s\n", lge_hw_rev_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_USB_ID) + return sprintf(buf, "%s\n", lge_usb_id_text[value.intval]); +#endif else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) return sprintf(buf, "%s\n", value.strval); @@ -130,6 +191,138 @@ static ssize_t power_supply_store_property(struct device *dev, return count; } +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY +extern int pseudo_batt_set(struct pseudo_batt_info_type*); + +static ssize_t pseudo_batt_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret; + struct power_supply *psy = dev_get_drvdata(dev); + const ptrdiff_t off = attr - power_supply_attrs; + union power_supply_propval value; + + static char *pseudo_batt[] = { + "NORMAL", "PSEUDO", + }; + + ret = psy->get_property(psy, off, &value); + + if (ret < 0) { + if (ret != -ENODEV) + dev_err(dev, "driver failed to report `%s' property\n", + attr->attr.name); + return ret; + } + if (off == POWER_SUPPLY_PROP_PSEUDO_BATT) + return sprintf(buf, "[%s] \nusage: echo [mode] [ID] [therm] [temp] [volt] [cap] [charging] > pseudo_batt\n", + pseudo_batt[value.intval]); + + return 0; +} + +static ssize_t pseudo_batt_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -EINVAL; + struct pseudo_batt_info_type info; + + if (sscanf(buf, "%d %d %d %d %d %d %d", &info.mode, &info.id, &info.therm, + &info.temp, &info.volt, &info.capacity, &info.charging) != 7) + { + if(info.mode == 1) //pseudo mode + { + printk(KERN_ERR "usage : echo [mode] [ID] [therm] [temp] [volt] [cap] [charging] > pseudo_batt"); + goto out; + } + } + + pseudo_batt_set(&info); + ret = count; + +out: + return ret; + +} +#endif + +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +static ssize_t at_chg_complete_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + ssize_t ret; + struct power_supply *psy = dev_get_drvdata(dev); + const ptrdiff_t off = attr - power_supply_attrs; + union power_supply_propval value; + + ret = psy->get_property(psy, off, &value); + + if (value.intval == 100) { + ret = snprintf(buf, 3, "%d\n", 0); + pr_info("[Diag] buf = %s, gauge==100\n", buf); + } else { + ret = snprintf(buf, 3, "%d\n", 1); + pr_info("[Diag] buf = %s, gauge<=100\n", buf); + } + + return ret; +} + +static ssize_t at_chg_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + struct power_supply *psy = dev_get_drvdata(dev); + const ptrdiff_t off = attr - power_supply_attrs; + union power_supply_propval value; + + ret = psy->get_property(psy, off, &value); + + if (value.intval == 1) { + ret = snprintf(buf, 3, "%d\n", 1); + pr_info("[Diag] true ! buf = %s, charging=1\n", buf); + } else { + ret= snprintf(buf, 3, "%d\n", 0); + pr_info("[Diag] false ! buf = %s, charging=0\n", buf); + } + + return ret; +} + +extern int chg_status_set(int value); + +static ssize_t at_chg_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + + if (!count) { + pr_err("[Diag] count 0 error\n"); + return -EINVAL; + } + + if (strncmp(buf, "0", 1) == 0) { + /* stop charging */ + pr_info("[Diag] stop charging start\n"); + ret = chg_status_set(false); + + } else if (strncmp(buf, "1", 1) == 0) { + /* start charging */ + pr_info("[Diag] start charging start\n"); + ret = chg_status_set(true); + } + + ret = count; + + return ret; +} +#endif + /* Must be in the same order as POWER_SUPPLY_PROP_* */ static struct device_attribute power_supply_attrs[] = { /* Properties of type `int' */ @@ -174,8 +367,10 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(capacity), POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), +#ifndef CONFIG_LGE_PM POWER_SUPPLY_ATTR(temp_cool), POWER_SUPPLY_ATTR(temp_warm), +#endif POWER_SUPPLY_ATTR(temp_ambient), POWER_SUPPLY_ATTR(time_to_empty_now), POWER_SUPPLY_ATTR(time_to_empty_avg), @@ -183,8 +378,25 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(type), POWER_SUPPLY_ATTR(scope), +#ifndef CONFIG_LGE_PM POWER_SUPPLY_ATTR(system_temp_level), +#endif POWER_SUPPLY_ATTR(resistance), +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + DIAG_CHCOMP_ATTR(chcomp), + DIAG_CHARGE_ATTR(charge), + POWER_SUPPLY_ATTR(hw_rev), + POWER_SUPPLY_ATTR(usb_id), +#endif +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + PSEUDO_BATT_ATTR(pseudo_batt), +#endif +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + POWER_SUPPLY_ATTR(valid_batt_id), +#endif +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + POWER_SUPPLY_ATTR(vzw_chg), +#endif /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c index fcb2c9e011ce..b0e5a1138b37 100644 --- a/drivers/power/qpnp-bms.c +++ b/drivers/power/qpnp-bms.c @@ -28,6 +28,10 @@ #include #include +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER +#include +#endif + /* BMS Register Offsets */ #define REVISION1 0x0 #define REVISION2 0x1 @@ -285,6 +289,14 @@ struct qpnp_bms_chip { struct qpnp_vadc_chip *vadc_dev; struct qpnp_iadc_chip *iadc_dev; struct qpnp_adc_tm_chip *adc_tm_dev; +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING + int rescale_soc; + unsigned long last_rescale_soc_time; +#endif +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + unsigned long last_iavg_cal_time; + int last_uuc_uah; +#endif }; static struct of_device_id qpnp_bms_match_table[] = { @@ -297,6 +309,9 @@ static char *qpnp_bms_supplicants[] = { }; static enum power_supply_property msm_bms_power_props[] = { +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + POWER_SUPPLY_PROP_ONLINE, +#endif POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CURRENT_NOW, @@ -304,6 +319,10 @@ static enum power_supply_property msm_bms_power_props[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + POWER_SUPPLY_PROP_CHCOMP, + POWER_SUPPLY_PROP_CHARGE, +#endif POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CYCLE_COUNT, }; @@ -311,7 +330,19 @@ static enum power_supply_property msm_bms_power_props[] = { static int discard_backup_fcc_data(struct qpnp_bms_chip *chip); static void backup_charge_cycle(struct qpnp_bms_chip *chip); +#ifdef CONFIG_LGE_PM_VZW_LLK +extern bool external_qpnp_chg_is_usb_chg_plugged_in(void); +extern int32_t vzw_llk_enable_charging(bool enable); +#endif + static bool bms_reset; +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING +static int cntSOC = 0; +static int bms_long_time_sleep = 0; +#endif +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME +static int get_current_time(unsigned long *now_tm_sec); +#endif static int qpnp_read_wrapper(struct qpnp_bms_chip *chip, u8 *val, u16 base, int count) @@ -955,6 +986,15 @@ static void reset_for_new_battery(struct qpnp_bms_chip *chip, int batt_temp) } } +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO +#include + +extern bool is_eoc_work_stop; +bool is_enter_first = true; + +void qpnp_goto_suspend_for_chg_logo(void); +#endif + #define SIGN(x) ((x) < 0 ? -1 : 1) #define UV_PER_SPIN 50000 static int find_ocv_for_pc(struct qpnp_bms_chip *chip, int batt_temp, int pc) @@ -1011,11 +1051,23 @@ static int find_ocv_for_pc(struct qpnp_bms_chip *chip, int batt_temp, int pc) #define OCV_RAW_UNINITIALIZED 0xFFFF #define MIN_OCV_UV 2000000 +#ifdef CONFIG_LGE_PM_BMS_CC_ACCURACY_PATCH +#define CC_ACCURACY_LOW_SOC_THRESHOLD 94 +#define VBATDET_DELTA_UV 100000 +#endif static int read_soc_params_raw(struct qpnp_bms_chip *chip, struct raw_soc_params *raw, int batt_temp) { int warm_reset, rc; +#ifdef CONFIG_LGE_PM_BMS_FACTORY_TEST + int boot_mode = 0; + int is_factory_cable = 0; +#endif +#ifdef CONFIG_LGE_PM_BMS_CC_ACCURACY_PATCH + int vbat_uv = 0; + int ibat_ua = 0; +#endif mutex_lock(&chip->bms_output_lock); @@ -1042,15 +1094,32 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip, convert_and_store_ocv(chip, raw, batt_temp); pr_debug("PON_OCV_UV = %d, cc = %llx\n", chip->last_ocv_uv, raw->cc); + +#ifdef CONFIG_LGE_PM_BMS_FACTORY_TEST + boot_mode = lge_get_boot_mode(); + printk("[qpnp-bms PON_OCV] PON_OVC_UV = %d, cc = %llx, boot_mode = %d\n", + chip->last_ocv_uv, raw->cc, boot_mode); + if( (boot_mode == LGE_BOOT_MODE_QEM_56K) || + (boot_mode == LGE_BOOT_MODE_QEM_130K) || + (boot_mode == LGE_BOOT_MODE_QEM_910K) ){ + is_factory_cable = 1; + } +#endif warm_reset = qpnp_pon_is_warm_reset(); if (raw->last_good_ocv_uv < MIN_OCV_UV - || warm_reset > 0) { + || warm_reset > 0 +#ifdef CONFIG_LGE_PM_BMS_FACTORY_TEST + || is_factory_cable == 1 +#endif + ) { pr_debug("OCV is stale or bad, estimating new OCV.\n"); chip->last_ocv_uv = estimate_ocv(chip); raw->last_good_ocv_uv = chip->last_ocv_uv; reset_cc(chip, CLEAR_CC | CLEAR_SHDW_CC); pr_debug("New PON_OCV_UV = %d, cc = %llx\n", chip->last_ocv_uv, raw->cc); + pr_info("New PON_OCV_UV = %d, cc = %llx\n", + chip->last_ocv_uv, raw->cc); } } else if (chip->new_battery) { /* if a new battery was inserted, estimate the ocv */ @@ -1082,6 +1151,32 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip, raw->last_good_ocv_uv = chip->last_ocv_uv; } +#ifdef CONFIG_LGE_PM_BMS_CC_ACCURACY_PATCH + rc = get_simultaneous_batt_v_and_i(chip, &ibat_ua, &vbat_uv); + if (rc < 0) { + pr_err("simultaneous vbat ibat failed err = %d\n", rc); + return 0; + } + + pr_debug("[qpnp-bms CC Accuracy] vbat_uv = %d, is_battery_full = %d, factor = %d\n", + vbat_uv, is_battery_full(chip), CC_ACCURACY_LOW_SOC_THRESHOLD); + if(is_battery_full(chip) && + (chip->calculated_soc <= CC_ACCURACY_LOW_SOC_THRESHOLD + 2 ) && + (chip->calculated_soc >= CC_ACCURACY_LOW_SOC_THRESHOLD) && + (vbat_uv >= chip->max_voltage_uv - VBATDET_DELTA_UV)){ + + chip->last_ocv_uv = estimate_ocv(chip); + raw->last_good_ocv_uv = chip->last_ocv_uv; + raw->cc = 0; + raw->shdw_cc = 0; + reset_cc(chip, CLEAR_CC | CLEAR_SHDW_CC); + chip->last_ocv_temp = batt_temp; + chip->software_cc_uah = 0; + chip->software_shdw_cc_uah = 0; + chip->last_cc_uah = INT_MIN; + printk("[qpnp-bms CC Accuracy] update new estimated ocv : chip->last_ocv_uv = %d\n", chip->last_ocv_uv); + } +#endif /* stop faking a high OCV if we get a new OCV */ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) chip->ocv_reading_at_100 = OCV_RAW_UNINITIALIZED; @@ -1351,6 +1446,9 @@ static int adjust_uuc(struct qpnp_bms_chip *chip, } #define MIN_IAVG_MA 250 +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME +#define MIN_IAVG_CAL_TIME 20 +#endif static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip, struct soc_params *params, int batt_temp) @@ -1359,7 +1457,10 @@ static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip, int i; int uuc_iavg_ma = params->iavg_ua / 1000; int pc_unusable; - +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + int time_since_last_iavg_cal = 0; + unsigned long now_tm_sec = 0; +#endif /* * if called first time, fill all the samples with * the shutdown_iavg_ma @@ -1374,6 +1475,16 @@ static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip, chip->iavg_num_samples = IAVG_SAMPLES; } +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + get_current_time(&now_tm_sec); + time_since_last_iavg_cal = now_tm_sec - chip->last_iavg_cal_time; + pr_debug("[BMS_DEBUG] chip->last_iavg_cal_time = %ld, time_since_last_iavg_cal = %d\n", + chip->last_iavg_cal_time, time_since_last_iavg_cal); + if( (time_since_last_iavg_cal < MIN_IAVG_CAL_TIME) && !chip->first_time_calc_uuc){ + goto out; + } + get_current_time(&chip->last_iavg_cal_time); +#endif if (params->delta_time_s >= IAVG_MINIMAL_TIME) { /* * if charging use a nominal avg current to keep @@ -1417,7 +1528,15 @@ static int calculate_unusable_charge_uah(struct qpnp_bms_chip *chip, uuc_uah_iavg = adjust_uuc(chip, params, pc_unusable, uuc_uah_iavg, batt_temp); + +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + chip->last_uuc_uah = uuc_uah_iavg; +#endif return uuc_uah_iavg; +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + out: + return chip->last_uuc_uah; +#endif } static s64 find_ocv_charge_for_soc(struct qpnp_bms_chip *chip, @@ -1761,10 +1880,34 @@ static int report_voltage_based_soc(struct qpnp_bms_chip *chip) return chip->prev_voltage_based_soc; } +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING +int cal_rnd_avg(int *soc_value, int unit_num) +{ + int sum = 0; + int avr = 0; + int i; + + for(i = 0; i < unit_num; i++){ + sum += soc_value[i]; + pr_debug("average soc[%d] = %d\n", i, soc_value[i]); + } + + avr = ((sum/unit_num)*10+5)/10; + + return avr; +} +#endif + #define SOC_CATCHUP_SEC_MAX 600 #define SOC_CATCHUP_SEC_PER_PERCENT 60 #define MAX_CATCHUP_SOC (SOC_CATCHUP_SEC_MAX / SOC_CATCHUP_SEC_PER_PERCENT) #define SOC_CHANGE_PER_SEC 5 + +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING +#define SOC_RESCALING_FACTOR 100/94 +#define I_MAX 10 /*numner of SOC averaging unit*/ +#define MAX_SLEEP_AVG_CAL_TIME 120 +#endif #define REPORT_SOC_WAIT_MS 10000 static int report_cc_based_soc(struct qpnp_bms_chip *chip) { @@ -1776,7 +1919,14 @@ static int report_cc_based_soc(struct qpnp_bms_chip *chip) int batt_temp; int rc; bool charging, charging_since_last_report; - +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING + static int soc_buf[I_MAX]={0,}; + static int idxBuf = 0; + int idxsoc = 0; + int avgSOC = 0; + unsigned long current_time = 0; + int time_since_last_rescale_soc_sec = 0; +#endif rc = wait_event_interruptible_timeout(chip->bms_wait_queue, chip->calculated_soc != -EINVAL, round_jiffies_relative(msecs_to_jiffies @@ -1887,9 +2037,54 @@ static int report_cc_based_soc(struct qpnp_bms_chip *chip) backup_soc_and_iavg(chip, batt_temp, chip->last_soc); pr_debug("Reported SOC = %d\n", chip->last_soc); chip->t_soc_queried = now; + +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING + chip->rescale_soc = chip->last_soc*SOC_RESCALING_FACTOR; + if(chip->rescale_soc > 100) + chip->rescale_soc = 100; + + // average of last 10 measured comp_soc value + if (cntSOC == 0){//access once + if(bms_long_time_sleep != 1){ + for(idxsoc=0; idxsocrescale_soc; + } + avgSOC = chip->rescale_soc; + cntSOC = I_MAX; + idxBuf = 1; + get_current_time(&chip->last_rescale_soc_time); + pr_debug("[first cal avg soc] get_current_time = %lu\n", chip->last_rescale_soc_time); + } + }else{ + if (idxBuf >= I_MAX){ + idxBuf = 0; + } + get_current_time(¤t_time); + time_since_last_rescale_soc_sec = current_time - chip->last_rescale_soc_time; + if( (abs(time_since_last_rescale_soc_sec) > 1)&& + (abs(time_since_last_rescale_soc_sec) < MAX_SLEEP_AVG_CAL_TIME) ){ + soc_buf[idxBuf] = chip->rescale_soc; + idxBuf++; + get_current_time(&chip->last_rescale_soc_time); + pr_debug("[add avg soc] get_current_time = %lu, since = %d\n", chip->last_rescale_soc_time, time_since_last_rescale_soc_sec); + }else if(abs(time_since_last_rescale_soc_sec) >= MAX_SLEEP_AVG_CAL_TIME){ + cntSOC = 0; + bms_long_time_sleep = 1; + schedule_work(&chip->recalc_work); + pr_debug("[BMS Sleep long time] time_since_last_rescale_soc_sec = %d\n", time_since_last_rescale_soc_sec); + } + } + avgSOC = cal_rnd_avg(soc_buf, I_MAX); + chip->rescale_soc = avgSOC; +#endif mutex_unlock(&chip->last_soc_mutex); +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING + pr_debug("[BMS_DEBUG] Rescale SOC = %d\n", chip->rescale_soc); + return chip->rescale_soc; +#else return soc; +#endif } static int report_state_of_charge(struct qpnp_bms_chip *chip) @@ -2189,7 +2384,10 @@ static int adjust_soc(struct qpnp_bms_chip *chip, struct soc_params *params, ibat_ua, vbat_uv, ocv_est_uv, pc_est, soc_est, n, delta_ocv_uv, chip->last_ocv_uv, pc_new, soc_new, params->rbatt_mohm, slope); - + printk("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, pc_new = %d, pc = %d, soc_new = %d, rbatt = %d, slope = %d\n", + ibat_ua, vbat_uv, ocv_est_uv, pc_est, + soc_est, n, delta_ocv_uv, chip->last_ocv_uv, + pc_new, pc, soc_new, params->rbatt_mohm, slope); return soc; } @@ -2365,7 +2563,8 @@ static int calculate_state_of_charge(struct qpnp_bms_chip *chip, int batt_temp) { struct soc_params params; - int soc, previous_soc, shutdown_soc, new_calculated_soc; + int soc=0; + int previous_soc, shutdown_soc, new_calculated_soc; int remaining_usable_charge_uah; calculate_soc_params(chip, raw, ¶ms, batt_temp); @@ -2459,6 +2658,27 @@ static int calculate_state_of_charge(struct qpnp_bms_chip *chip, mutex_unlock(&chip->last_soc_mutex); wake_up_interruptible(&chip->bms_wait_queue); +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO +/* At high temperature, device should be woke up as soon as possible for safety. */ + if ((batt_temp >= 570) && (chip->bms_psy_registered)) { + pr_debug("[LGE] High battery temperature! batt_temp = %d\n", batt_temp); + power_supply_changed(&chip->bms_psy); + } +#endif + +#ifdef CONFIG_LGE_PM_VZW_LLK + if (external_qpnp_chg_is_usb_chg_plugged_in()) { + if (chip->rescale_soc == 35) { + vzw_llk_enable_charging(0); + printk(KERN_INFO "%s : VZW LLK Charging Stop!!\n", __func__); + } + else if (chip->rescale_soc == 30) { + vzw_llk_enable_charging(1); + printk(KERN_INFO "%s : VZW LLK Charging Enable!!\n", __func__); + } + } +#endif + if (new_calculated_soc != previous_soc && chip->bms_psy_registered) { power_supply_changed(&chip->bms_psy); pr_debug("power supply changed\n"); @@ -2473,7 +2693,10 @@ static int calculate_state_of_charge(struct qpnp_bms_chip *chip, get_current_time(&chip->last_recalc_time); chip->first_time_calc_soc = 0; - chip->first_time_calc_uuc = 0; + chip->first_time_calc_uuc = 0; + + printk("New_cal_SOC = %d, SOC = %d, batt_temp = %d, rbatt = %d, fcc_uah = %d, ocv_charge_uah = %d, uuc_uah = %d, cc_uah = %d, iavg_ua = %d\n", + new_calculated_soc, soc, batt_temp, params.rbatt_mohm, params.fcc_uah, params.ocv_charge_uah, params.uuc_uah, params.cc_uah, params.iavg_ua); return chip->calculated_soc; } @@ -2588,6 +2811,15 @@ static int recalculate_soc(struct qpnp_bms_chip *chip) mutex_unlock(&chip->last_ocv_uv_mutex); } } + +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + if(lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO && is_enter_first && (is_eoc_work_stop || chip->done_charging)){ + is_enter_first = false; + + qpnp_goto_suspend_for_chg_logo(); + } +#endif + bms_relax(&chip->soc_wake_source); return soc; } @@ -2599,6 +2831,9 @@ static void recalculate_work(struct work_struct *work) recalc_work); recalculate_soc(chip); +#ifdef CONFIG_LGE_PM_BATTERY_SOC_RESCALING + bms_long_time_sleep = 0; +#endif } static int get_calculation_delay_ms(struct qpnp_bms_chip *chip) @@ -3365,6 +3600,11 @@ static int qpnp_bms_power_get_property(struct power_supply *psy, bms_psy); switch (psp) { +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + case POWER_SUPPLY_PROP_ONLINE: + val->intval = is_battery_present(chip); + break; +#endif case POWER_SUPPLY_PROP_CAPACITY: val->intval = get_prop_bms_capacity(chip); break; @@ -3386,6 +3626,14 @@ static int qpnp_bms_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval = get_prop_bms_charge_full_design(chip); break; +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + case POWER_SUPPLY_PROP_CHCOMP: + val->intval = get_prop_bms_capacity(chip); + break; + case POWER_SUPPLY_PROP_CHARGE: + val->intval = is_battery_charging(chip); + break; +#endif case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = get_prop_bms_charge_full(chip); break; @@ -3542,6 +3790,9 @@ static void load_shutdown_data(struct qpnp_bms_chip *chip) pr_debug("Ignoring shutdown SoC: invalid = %d, offmode = %d, out_of_limit = %d\n", invalid_stored_soc, offmode_battery_replaced, shutdown_soc_out_of_limit); + pr_info("Ignoring shutdown SoC: invalid = %d, offmode = %d, out_of_limit = %d\n", + invalid_stored_soc, offmode_battery_replaced, + shutdown_soc_out_of_limit); } else { chip->shutdown_iavg_ma = read_shutdown_iavg_ma(chip); chip->shutdown_soc = shutdown_soc; @@ -3553,6 +3804,12 @@ static void load_shutdown_data(struct qpnp_bms_chip *chip) chip->shutdown_iavg_ma, chip->shutdown_soc_invalid, chip->battery_removed); + pr_info("raw_soc = %d shutdown_soc = %d shutdown_iavg = %d shutdown_soc_invalid = %d, battery_removed = %d\n", + calculated_soc, + chip->shutdown_soc, + chip->shutdown_iavg_ma, + chip->shutdown_soc_invalid, + chip->battery_removed); } static irqreturn_t bms_ocv_thr_irq_handler(int irq, void *_chip) @@ -3578,6 +3835,9 @@ static irqreturn_t bms_sw_cc_thr_irq_handler(int irq, void *_chip) static int64_t read_battery_id(struct qpnp_bms_chip *chip) { +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + return read_lge_battery_id(); +#else int rc; struct qpnp_vadc_result result; @@ -3589,14 +3849,183 @@ static int64_t read_battery_id(struct qpnp_bms_chip *chip) } return result.physical; +#endif + } static int set_battery_data(struct qpnp_bms_chip *chip) { +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + int32_t battery_id; +#else int64_t battery_id; int rc = 0, dt_data = false; struct bms_battery_data *batt_data; struct device_node *node; +#endif + +#ifdef CONFIG_LGE_PM_BATTERY_PROFILE_DATA // Neet to change later + struct bms_battery_data *batt_data; + +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + battery_id = read_battery_id(chip); + +#ifdef CONFIG_LGE_PM_BATTERY_CAPACITY_1540mAh + switch ( battery_id ){ + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_DS2704_L : // FALL THROUGH + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + case BATT_ID_ISL6296_L : // FALL THROUGH + case BATT_ID_ISL6296_C : // FALL THROUGH + default : + batt_data = &LGC_BL44JR_1540_data; + pr_info("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGC_1540mAh for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_1700mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_L : // FALL THROUGH + batt_data = &LG_LGC_BL44JH_1700_data; + pr_info("[BATTERY PROFILE] Using battery profile - LGChem_1700mAh for id(%d)\n",battery_id); + break; + case BATT_ID_ISL6296_L : // FALL THROUGH + batt_data = &LG_TOCAD_BL44JH_1700_data; + pr_info("[BATTERY PROFILE] Using battery profile - TOCAD_1700mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + case BATT_ID_ISL6296_C : // FALL THROUGH + default : + batt_data = &LG_LGC_BL44JH_1700_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LG_LGC_BL44JH_1700_data for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2440mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_DS2704_L : + batt_data = &LGE_BL_59UH_2440mAh_LG_Chem_data; + pr_info("[BATTERY PROFILE] Using battery profile - LGChem_2440mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_C : + batt_data = &LGE_BL_59UH_2440mAh_TOCAD_data; + pr_info("[BATTERY PROFILE] Using battery profile - TOCAD_2440mAh for id(%d)\n",battery_id); + break; + case BATT_ID_ISL6296_N : // FALL THROUGH + case BATT_ID_ISL6296_L : + batt_data = &LGE_BL_59UH_2440mAh_TOCAD_data; + pr_info("[BATTERY PROFILE] Using battery profile - TOCAD_2440mAh for id(%d)\n",battery_id); + break; + case BATT_ID_ISL6296_C : + batt_data = &LGE_BL_59UH_2440mAh_LG_Chem_data; + pr_info("[BATTERY PROFILE] Using battery profile - LGChem_2440mAh for id(%d)\n",battery_id); + break; + default : + batt_data = &LGE_BL_59UH_2440mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGE_BL_59UH_2440mAh_LG_Chem_data for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2540mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_L : // FALL THROUGH + case BATT_ID_ISL6296_C : + batt_data = &LGE_BL_54SH_2540mAh_LG_Chem_data; + pr_info("[BATTERY PROFILE] Using battery profile - LGChem_2540mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_L : + batt_data = &LGE_BL_54SH_2540mAh_LG_Sanyo_data; + pr_info("[BATTERY PROFILE] Using battery profile - Sanyo_2540mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + default : + batt_data = &LGE_BL_54SH_2540mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGChem_2540mAh for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_3000mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_L : // FALL THROUGH + case BATT_ID_ISL6296_C : // FALL THROUGH + batt_data = &LGE_BL_64SH_3000mAh_LG_Chem_data; + pr_info("[BATTERY PROFILE] Using battery profile - LGChem_3000mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_L : // FALL THROUGH + batt_data = &LGE_BL_64SH_3000mAh_Technohill_data; + pr_info("[BATTERY PROFILE] Using battery profile - Technohill_3000mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + default : + batt_data = &LGE_BL_64SH_3000mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGChem_3000mAh for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2100mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_DS2704_L : // FALL THROUGH + batt_data = &LGE_LGC_2040mAH_data; + pr_err("[BATTERY PROFILE] Using default profile - LGChem_2100mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + case BATT_ID_ISL6296_L : // FALL THROUGH +#ifdef CONFIG_LGE_PM_BATTERY_HITACI_2100mAh + batt_data = &LGE_Hitaci_2040mAh_data; + pr_err("[BATTERY PROFILE] Using default profile - Hitaci_2100mAh for id(%d)\n",battery_id); +#else + batt_data = &LGE_Tocad_2040mAh_data; + pr_err("[BATTERY PROFILE] Using default profile - Tocad_2100mAh for id(%d)\n",battery_id); +#endif + break; + case BATT_ID_ISL6296_C : // FALL THROUGH + default : + batt_data = &LGE_LGC_2040mAH_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGChem_2100mAh for id(%d)\n",battery_id); + break; + } +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_3200mAh) + switch ( battery_id ){ + case BATT_ID_DS2704_N : // FALL THROUGH + case BATT_ID_DS2704_L : // FALL THROUGH + batt_data = &LGE_BL_47TH_3200mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] Using default profile - LGChem_3200mAh for id(%d)\n",battery_id); + break; + case BATT_ID_DS2704_C : // FALL THROUGH + case BATT_ID_ISL6296_N : // FALL THROUGH + case BATT_ID_ISL6296_L : // FALL THROUGH + batt_data = &LGE_BL_47TH_3200mAh_Tocad_data; + pr_err("[BATTERY PROFILE] Using default profile - Tocad_3200mAh for id(%d)\n",battery_id); + break; + case BATT_ID_ISL6296_C : // FALL THROUGH + default : + batt_data = &LGE_BL_47TH_3200mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] No battery ID matching\nUsing default profile - LGChem_3200mAh for id(%d)\n",battery_id); + break; + } +#else + batt_data = &LGE_BL_54SH_2540mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] No battery capacity defconfig\nUsing default profile - LGChem_2540mAh for id(%d)\n",battery_id); +#endif + +#else // Not PM battery ID checker + // Set Default Battery Profile + batt_data = &LGE_BL_54SH_2540mAh_LG_Chem_data; + pr_err("[BATTERY PROFILE] This version doesn't support BATTERY ID CHECKER\nUsing default profile-LGChem_2540mAh\n"); + +#endif +#if 1 //FIXME + goto assign_data; +#endif +#else // Not PM battery profile + pr_err ("[BATTERY PROFILE] This version doesn't support BATTERY ID PROFILE use QCT Battery Data\n"); + + // From Here : QCT Original Data We don't use this if (chip->batt_type == BATT_DESAY) { batt_data = &desay_5200_data; @@ -3662,6 +4091,7 @@ static int set_battery_data(struct qpnp_bms_chip *chip) batt_data = &palladium_1500_data; } } +#endif assign_data: chip->fcc_mah = batt_data->fcc; @@ -3674,6 +4104,7 @@ static int set_battery_data(struct qpnp_bms_chip *chip) chip->rbatt_capacitive_mohm = batt_data->rbatt_capacitive_mohm; chip->flat_ocv_threshold_uv = batt_data->flat_ocv_threshold_uv; +#ifndef CONFIG_LGE_PM_BATTERY_PROFILE_DATA /* Override battery properties if specified in the battery profile */ if (batt_data->max_voltage_uv >= 0 && dt_data) chip->max_voltage_uv = batt_data->max_voltage_uv; @@ -3695,6 +4126,7 @@ static int set_battery_data(struct qpnp_bms_chip *chip) if (dt_data) devm_kfree(chip->dev, batt_data); +#endif return 0; } @@ -3838,6 +4270,18 @@ static inline int bms_read_properties(struct qpnp_bms_chip *chip) pr_debug("ignore_shutdown_soc:%d, use_voltage_soc:%d\n", chip->ignore_shutdown_soc, chip->use_voltage_soc); pr_debug("use external rsense: %d\n", chip->use_external_rsense); + pr_info("[BMS_DEBUG][bms_read_properties] dts data: r_sense_uohm:%d, v_cutoff_uv:%d, max_v:%d\n", + chip->r_sense_uohm, chip->v_cutoff_uv, + chip->max_voltage_uv); + pr_info("[BMS_DEBUG][bms_read_properties] r_conn:%d, shutdown_soc: %d, adjust_soc_low:%d\n", + chip->r_conn_mohm, chip->shutdown_soc_valid_limit, + chip->adjust_soc_low_threshold); + pr_info("[BMS_DEBUG][bms_read_properties] chg_term_ua:%d, batt_type:%d\n", + chip->chg_term_ua, + chip->batt_type); + pr_info("[BMS_DEBUG][bms_read_properties] ignore_shutdown_soc:%d, use_voltage_soc:%d\n", + chip->ignore_shutdown_soc, chip->use_voltage_soc); + pr_info("[BMS_DEBUG][bms_read_properties] use external rsense: %d\n", chip->use_external_rsense); return 0; } @@ -4293,6 +4737,13 @@ static int __devinit qpnp_bms_probe(struct spmi_device *spmi) goto error_setup; } +#ifdef CONFIG_LGE_PM + rc = bms_request_irqs(chip); + if (rc) { + pr_err("error requesting bms irqs, rc = %d\n", rc); + goto unregister_dc; + } +#endif battery_insertion_check(chip); batfet_status_check(chip); battery_status_check(chip); @@ -4326,11 +4777,18 @@ static int __devinit qpnp_bms_probe(struct spmi_device *spmi) goto unregister_dc; } +#ifndef CONFIG_LGE_PM + /* we changed code location before calculate_soc_work to avoid warning message for ocv_thr/sw_cc_thr IRQ */ rc = bms_request_irqs(chip); if (rc) { pr_err("error requesting bms irqs, rc = %d\n", rc); goto unregister_dc; } +#endif + +#ifdef CONFIG_LGE_PM_BMS_MIN_IAVG_CAL_TIME + get_current_time(&chip->last_iavg_cal_time); +#endif pr_info("probe success: soc =%d vbatt = %d ocv = %d r_sense_uohm = %u warm_reset = %d\n", get_prop_bms_capacity(chip), vbatt, chip->last_ocv_uv, @@ -4375,6 +4833,8 @@ static int bms_resume(struct device *dev) struct qpnp_bms_chip *chip = dev_get_drvdata(dev); rc = get_current_time(&tm_now_sec); + pr_debug("[BMS_DEBUG] bms_resume current_time = %ld, last_recalc_time = %ld, delta = %ld\n", + tm_now_sec, chip->last_recalc_time, tm_now_sec - chip->last_recalc_time); if (rc) { pr_err("Could not read current time: %d\n", rc); } else { diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c index 1223fe0a2098..891f9f457271 100644 --- a/drivers/power/qpnp-charger.c +++ b/drivers/power/qpnp-charger.c @@ -33,6 +33,40 @@ #include #include +#include +#include + +#ifdef CONFIG_LGE_USING_CHG_LED +#include +#endif + +/* */ +#ifdef CONFIG_LGE_PM +#include "../../arch/arm/mach-msm/smd_private.h" +#include +#endif +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO +#ifdef CONFIG_MACH_MSM8926_X5_SPR +#include +#else +#include +#endif +#define MONITOR_BATTEMP_POLLING_PERIOD (60*HZ) +#endif +/* */ + +/* */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP +#include +#endif +/* */ + +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO +#include +#include +#endif + + /* Interrupt offsets */ #define INT_RT_STS(base) (base + 0x10) #define INT_SET_TYPE(base) (base + 0x11) @@ -55,6 +89,9 @@ #define CHGR_VDD_MAX 0x40 #define CHGR_VDD_SAFE 0x41 #define CHGR_VDD_MAX_STEP 0x42 +#ifdef CONFIG_LGE_PM_NEED_TO_MONITORING_QCT_PATCH // Removing 100mV Drop in VDDMAX +#define CHGR_VDDMAX_GSM_ADJ 0x43 +#endif #define CHGR_IBAT_MAX 0x44 #define CHGR_IBAT_SAFE 0x45 #define CHGR_VIN_MIN 0x47 @@ -149,7 +186,11 @@ #define USB_VALID_DEB_20MS 0x03 #define BUCK_VBAT_REG_NODE_SEL_BIT BIT(0) #define VREF_BATT_THERM_FORCE_ON 0xC0 +#ifdef CONFIG_ARCH_MSM8610 // Need to verify +#define BAT_IF_BPD_CTRL_SEL 0x02 +#else #define BAT_IF_BPD_CTRL_SEL 0x03 +#endif #define VREF_BAT_THM_ENABLED_FSM 0x80 #define REV_BST_DETECTED BIT(0) #define BAT_THM_EN BIT(1) @@ -214,6 +255,41 @@ #define BOOST_FLASH_WA BIT(1) #define POWER_STAGE_WA BIT(2) +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG +enum vzw_chg_state { + VZW_NO_CHARGER = 0, + VZW_NORMAL_CHARGING = 1, + VZW_NOT_CHARGING = 2, + VZW_UNDER_CURRENT_CHARGING = 3, + VZW_USB_DRIVER_UNINSTALLED = 4, +#ifdef CONFIG_LGE_PM_VZW_LLK + VZW_LLK_NOT_CHARGING = 5, +#endif +}; + +static enum vzw_chg_state chg_state = VZW_NO_CHARGER; + +#define NOT_PRESENT 0 +#define CHARGER_PRESENT 1 +#define UNKNOWN_PRESENT 2 +#define SLOW_PRESENT 3 +#define USB_PRESENT 4 + +static int vzw_chg_present = NOT_PRESENT; + +#define IS_OPEN_TA 0 +#define IS_USB_DRIVER_UNINSTALLED 1 +#define IS_USB_DRIVER_INSTALLED 2 +#define IS_USB_CHARGING_ENABLE 3 + +static int usb_chg_state = IS_USB_DRIVER_INSTALLED; +#ifdef CONFIG_LGE_PM_VZW_LLK +static int temp_state = 0; +#endif + +extern int lge_usb_config_finish; +extern void send_drv_state_uevent(int usb_drv_state); +#endif struct qpnp_chg_irq { int irq; unsigned long disabled; @@ -224,6 +300,30 @@ struct qpnp_chg_regulator { struct regulator_dev *rdev; }; +#ifdef CONFIG_LGE_USING_CHG_LED +/** + * pwm_config_data - pwm configuration data + * @pwm_device - pwm device + * @pwm_channel - pwm channel to be configured for led + * @pwm_period_us - period for pwm, in us + * @mode - mode the led operates in + * @old_duty_pcts - storage for duty pcts that may need to be reused + * @default_mode - default mode of LED as set in device tree + * @use_blink - use blink sysfs entry + * @blinking - device is currently blinking w/LPG mode + */ +struct pwm_config_data { + struct pwm_device *pwm_dev; + int pwm_channel; + u32 pwm_period_us; + struct pwm_duty_cycles *duty_cycles; + int *old_duty_pcts; + u8 mode; + u8 default_mode; + bool use_blink; + bool blinking; +}; +#endif /** * struct qpnp_chg_chip - device information * @dev: device pointer to access the parent @@ -247,7 +347,6 @@ struct qpnp_chg_regulator { * @max_voltage_mv: the max volts the batt should be charged up to * @min_voltage_mv: min battery voltage before turning the FET on * @batt_weak_voltage_mv: Weak battery voltage threshold - * @vbatdet_max_err_mv resume voltage hysterisis * @max_bat_chg_current: maximum battery charge current in mA * @warm_bat_chg_ma: warm battery maximum charge current in mA * @cool_bat_chg_ma: cool battery maximum charge current in mA @@ -324,7 +423,6 @@ struct qpnp_chg_chip { unsigned int max_voltage_mv; unsigned int min_voltage_mv; unsigned int batt_weak_voltage_mv; - unsigned int vbatdet_max_err_mv; int prev_usb_max_ma; int set_vddmax_mv; int delta_vddmax_mv; @@ -347,9 +445,23 @@ struct qpnp_chg_chip { unsigned int revision; unsigned int type; unsigned int tchg_mins; +#ifndef CONFIG_LGE_PM unsigned int thermal_levels; unsigned int therm_lvl_sel; unsigned int *thermal_mitigation; +#endif +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + struct delayed_work battemp_work; + struct wake_lock lcs_wake_lock; + enum lge_btm_states btm_state; + int pseudo_ui_chg; + int not_chg; + bool is_charger_changed_from_irq; + int current_batt_temp; +#ifdef CONFIG_LGE_PM_THERMAL + int chg_current_te; +#endif +#endif struct power_supply dc_psy; struct power_supply *usb_psy; struct power_supply *bms_psy; @@ -360,12 +472,29 @@ struct qpnp_chg_chip { struct work_struct adc_disable_work; struct delayed_work arb_stop_work; struct delayed_work eoc_work; - struct delayed_work usbin_health_check; + struct delayed_work usbin_health_check; +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + struct delayed_work pwr_key_monitor_for_chg_logo; +#endif +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START struct work_struct soc_check_work; +#endif struct delayed_work aicl_check_work; + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + struct delayed_work input_current_check_work; + struct work_struct cancel_input_current_check_work; + bool is_vzw_slow_charging; +#endif + struct work_struct insertion_ocv_work; struct qpnp_chg_regulator otg_vreg; struct qpnp_chg_regulator boost_vreg; +#ifdef CONFIG_LGE_PM + unsigned int ac_online; + unsigned int current_max; + bool chg_fail_irq_happen; +#endif struct qpnp_chg_regulator batfet_vreg; bool batfet_ext_en; struct work_struct batfet_lcl_work; @@ -379,14 +508,119 @@ struct qpnp_chg_chip { struct work_struct reduce_power_stage_work; bool power_stage_workaround_running; bool power_stage_workaround_enable; +/* */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP + int ext_ovp_gpio; +#endif +/* */ +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + bool from_temp_monitor_vbat_det_high; + struct work_struct resume_check_work; + struct work_struct batt_temp_cancel_work; +#endif + +#ifdef CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST + struct delayed_work property_work; +#endif +#ifdef CONFIG_LGE_PM + struct wake_lock uevent_wake_lock; +#endif +#ifdef CONFIG_LGE_USING_CHG_LED + struct led_classdev cdev; + struct pwm_config_data *pwm_cfg; +#endif }; - static struct of_device_id qpnp_charger_match_table[] = { { .compatible = QPNP_CHARGER_DEV_NAME, }, {} }; +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY +#define PSEUDO_BATT_MAX 700 +static struct qpnp_chg_chip *qpnp_chg; +struct pseudo_batt_info_type pseudo_batt_info = { + .mode = 0, +}; +#endif + +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +int testmode_stop_eoc = 0; +#endif + +#ifdef CONFIG_LGE_PM +#define LT_CABLE_56K 6 +#define LT_CABLE_130K 7 +#define LT_CABLE_910K 11 +#endif + +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP +bool lge_check_fast_chg_irq(void); +void lge_set_chg_path_to_external(void); +void lge_set_chg_path_to_internal(void); +#endif + +#ifdef CONFIG_LGE_PM_VZW_HIGH_TEMP_PWR_OFF +extern void write_high_temp_power_off(char *filename); +extern int read_high_temp_power_off(char *filename); +#endif + +/* */ +/* */ +#ifdef CONFIG_LGE_PM_USB_ID +static unsigned int cable_type; +static bool is_factory_cable(void) +{ + unsigned int cable_info; + cable_info = lge_pm_get_cable_type(); + + if ((cable_info == CABLE_56K || + cable_info == CABLE_130K || + cable_info == CABLE_910K)|| + (cable_type == LT_CABLE_56K || + cable_type == LT_CABLE_130K || + cable_type == LT_CABLE_910K)) + return 1; + else + return 0; +} + +extern void lge_pm_set_usb_id_handle(struct qpnp_vadc_chip *); +#endif +/* */ +/* */ + +/* */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP +enum +{ + EXT_OVP_CTRL_LOW = 0 , //Not using external ovp path + EXT_OVP_CTRL_HIGH = 1, //Using external ovp path +}; + +static void lge_pm_set_ext_ovp(int ext_ovp_gpio, bool on) +{ + if (gpio_is_valid(ext_ovp_gpio)) + gpio_set_value(ext_ovp_gpio, on); +} +#endif +/* */ + +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START +static void +lge_pm_batt_temp_cancel_work(struct work_struct *work) +{ + struct qpnp_chg_chip *chip = container_of(work, + struct qpnp_chg_chip, batt_temp_cancel_work); + + cancel_delayed_work_sync(&chip->battemp_work); + + chip->pseudo_ui_chg = 0; + if (wake_lock_active(&chip->lcs_wake_lock)) + wake_unlock(&chip->lcs_wake_lock); +} +#endif + enum bpd_type { BPD_TYPE_BAT_ID, BPD_TYPE_BAT_THM, @@ -571,6 +805,8 @@ qpnp_chg_is_boost_en_set(struct qpnp_chg_chip *chip) return (boost_en_ctl & BOOST_PWR_EN) ? 1 : 0; } +/* */ +#ifndef CONFIG_LGE_PM static int qpnp_chg_is_batt_temp_ok(struct qpnp_chg_chip *chip) { @@ -587,7 +823,7 @@ qpnp_chg_is_batt_temp_ok(struct qpnp_chg_chip *chip) return (batt_rt_sts & BAT_TEMP_OK_IRQ) ? 1 : 0; } - +#endif static int qpnp_chg_is_batt_present(struct qpnp_chg_chip *chip) { @@ -641,6 +877,13 @@ qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip) return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0; } +#ifdef CONFIG_LGE_PM_VZW_LLK +bool external_qpnp_chg_is_usb_chg_plugged_in(void) +{ + return qpnp_chg_is_usb_chg_plugged_in(qpnp_chg); +} +#endif + static bool qpnp_chg_is_ibat_loop_active(struct qpnp_chg_chip *chip) @@ -834,12 +1077,27 @@ qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA) int rc = 0; u8 usb_reg = 0, temp = 8; - if (mA < 0 || mA > QPNP_CHG_I_MAX_MAX_MA) { + pr_err("[LGE] qpnp_chg_iusbmax_set, current = %d\n", mA); + +//QCT patch through CN: 01346869, fix the issue of IUSB_MAX not being reverted to default correctly after charger detachment +//This patch will released official migraion verion +#ifdef CONFIG_LGE_PM + if (mA < 0 || mA > QPNP_CHG_I_MAX_MAX_MA){ +#else + if (mA < QPNP_CHG_I_MAX_MIN_100 + || mA > QPNP_CHG_I_MAX_MAX_MA) { +#endif pr_err("bad mA=%d asked to set\n", mA); return -EINVAL; } - if (mA <= QPNP_CHG_I_MAX_MIN_100) { +//QCT patch through CN: 01346869, fix the issue of IUSB_MAX not being reverted to default correctly after charger detachment +//This patch will release offiicial migrion version +#ifdef CONFIG_LGE_PM + if (mA == QPNP_CHG_I_MAX_MIN_100 || mA == 0) { +#else + if (mA == QPNP_CHG_I_MAX_MIN_100) { +#endif usb_reg = 0x00; pr_debug("current=%d setting %02x\n", mA, usb_reg); return qpnp_chg_write(chip, &usb_reg, @@ -1006,7 +1264,7 @@ qpnp_chg_charge_en(struct qpnp_chg_chip *chip, int enable) pr_debug("Battery not present, skipping\n"); return 0; } - pr_debug("charging %s\n", enable ? "enabled" : "disabled"); + pr_err("charging %s\n", enable ? "enabled" : "disabled"); return qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_CTRL, CHGR_CHG_EN, enable ? CHGR_CHG_EN : 0, 1); @@ -1096,8 +1354,11 @@ qpnp_chg_vbatdet_set(struct qpnp_chg_chip *chip, int vbatdet_mv) } temp = (vbatdet_mv - QPNP_CHG_VBATDET_MIN_MV) / QPNP_CHG_VBATDET_STEP_MV; - +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + pr_err("qpnp_chg_vbatdet_set voltage=%d setting %02x\n", vbatdet_mv, temp); +#else pr_debug("voltage=%d setting %02x\n", vbatdet_mv, temp); +#endif return qpnp_chg_write(chip, &temp, chip->chgr_base + CHGR_VBAT_DET, 1); } @@ -1158,13 +1419,13 @@ qpnp_chg_vbatdet_lo_irq_handler(int irq, void *_chip) u8 chg_sts = 0; int rc; - pr_debug("vbatdet-lo triggered\n"); + pr_err("vbatdet-lo triggered\n"); rc = qpnp_chg_read(chip, &chg_sts, INT_RT_STS(chip->chgr_base), 1); if (rc) pr_err("failed to read chg_sts rc=%d\n", rc); - pr_debug("chg_done chg_sts: 0x%x triggered\n", chg_sts); + pr_err("chg_done chg_sts: 0x%x triggered\n", chg_sts); if (!chip->charging_disabled && (chg_sts & FAST_CHG_ON_IRQ)) { schedule_delayed_work(&chip->eoc_work, msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); @@ -1185,6 +1446,29 @@ qpnp_chg_vbatdet_lo_irq_handler(int irq, void *_chip) return IRQ_HANDLED; } +#ifdef CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST +static void +qpnp_property_work(struct work_struct *work) +{ + int usb_present=false; + + struct delayed_work *dwork = to_delayed_work(work); + struct qpnp_chg_chip *chip = container_of(dwork, struct qpnp_chg_chip, property_work); + usb_present = qpnp_chg_is_usb_chg_plugged_in(chip); + + if ( chip->usb_present ^ usb_present ){ + pr_err("USB UVD Detection fail restored. %d -> %d\n",chip->usb_present, usb_present); + chip->usb_present = usb_present; + power_supply_set_present(chip->usb_psy, chip->usb_present); + } +} +#endif + +#ifdef CONFIG_LGE_PM // Using Interanl & External OVP control +void lge_power_supply_set_type(int supply_type); +int lge_power_supply_get_type(void); +#endif + #define ARB_STOP_WORK_MS 1000 static irqreturn_t qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip) @@ -1198,16 +1482,47 @@ qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip) if (rc) pr_err("failed to read usb_chgpth_sts rc=%d\n", rc); +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + pr_err("============ QPNP CHG GONE ==============\n"); + if(lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO) + { + pm_stay_awake(chip->dev); + } +#endif + pr_debug("chg_gone triggered\n"); - if ((qpnp_chg_is_usb_chg_plugged_in(chip) - || qpnp_chg_is_dc_chg_plugged_in(chip)) - && (usb_sts & CHG_GONE_IRQ)) { +/* */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP + lge_set_chg_path_to_internal(); +#endif + +#ifdef CONFIG_LGE_PM + chip->chg_fail_irq_happen = false; +#endif + +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + chip->is_charger_changed_from_irq = true; +#endif + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + chip->is_vzw_slow_charging = false; +#endif +/* */ + if (qpnp_chg_is_usb_chg_plugged_in(chip) && (usb_sts & CHG_GONE_IRQ)) { qpnp_chg_charge_en(chip, 0); qpnp_chg_force_run_on_batt(chip, 1); +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + schedule_work(&chip->resume_check_work); +#endif schedule_delayed_work(&chip->arb_stop_work, msecs_to_jiffies(ARB_STOP_WORK_MS)); } +#ifdef CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST + schedule_delayed_work(&chip->property_work, + msecs_to_jiffies(100)); +#endif + return IRQ_HANDLED; } @@ -1217,7 +1532,7 @@ qpnp_chg_usb_usb_ocp_irq_handler(int irq, void *_chip) struct qpnp_chg_chip *chip = _chip; int rc; - pr_debug("usb-ocp triggered\n"); + pr_err("usb-ocp triggered\n"); rc = qpnp_chg_masked_write(chip, chip->usb_chgpth_base + USB_OCP_CLR, @@ -1250,6 +1565,10 @@ qpnp_chg_vddmax_and_trim_set(struct qpnp_chg_chip *chip, int rc, trim_set; u8 vddmax = 0, trim = 0; +#ifdef CONFIG_LGE_PM_NEED_TO_MONITORING_QCT_PATCH // Removing 100mV Drop in VDDMAX + u8 temp = 0; +#endif + if (voltage < QPNP_CHG_VDDMAX_MIN || voltage > QPNP_CHG_V_MAX_MV) { pr_err("bad mV=%d asked to set\n", voltage); @@ -1262,7 +1581,10 @@ qpnp_chg_vddmax_and_trim_set(struct qpnp_chg_chip *chip, pr_err("Failed to write vddmax: %d\n", rc); return rc; } - +#ifdef CONFIG_LGE_PM_NEED_TO_MONITORING_QCT_PATCH // Removing 100mV Drop in VDDMA + temp = 0x4A; + qpnp_chg_write(chip, &temp, chip->chgr_base + CHGR_VDDMAX_GSM_ADJ, 1); +#endif rc = qpnp_chg_masked_write(chip, chip->buck_base + SEC_ACCESS, 0xFF, @@ -1366,7 +1688,7 @@ qpnp_chg_coarse_det_usb_irq_handler(int irq, void *_chip) host_mode = qpnp_chg_is_otg_en_set(chip); usb_coarse_det = qpnp_chg_check_usb_coarse_det(chip); - pr_debug("usb coarse-det triggered: %d host_mode: %d\n", + pr_err("usb coarse-det triggered: %d host_mode: %d\n", usb_coarse_det, host_mode); if (host_mode) @@ -1441,18 +1763,58 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) int usb_present, host_mode, usbin_health; u8 psy_health_sts; +#ifdef CONFIG_LGE_PM + if ( (chip->uevent_wake_lock.ws.name) != NULL ) + wake_lock_timeout(&chip->uevent_wake_lock, HZ*2); +#endif + usb_present = qpnp_chg_is_usb_chg_plugged_in(chip); host_mode = qpnp_chg_is_otg_en_set(chip); - pr_debug("usbin-valid triggered: %d host_mode: %d\n", + pr_err("usbin-valid triggered: %d host_mode: %d\n", usb_present, host_mode); /* In host mode notifications cmoe from USB supply */ if (host_mode) return IRQ_HANDLED; +#ifdef CONFIG_LGE_PM + /* Sometimes when USB cable is removed, usb_present value is still true + * because CHGR_STATUS register does not changed after remove USB cable + * And then usb_present check one more time when usb_present does not change */ + if(!(chip->usb_present ^ usb_present)) + { + usb_present = qpnp_chg_is_usb_chg_plugged_in(chip); + pr_err("need to check usbin-valid triggered: %d \n", usb_present); + } +#endif + if (chip->usb_present ^ usb_present) { chip->usb_present = usb_present; +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + chip->is_charger_changed_from_irq = true; + schedule_delayed_work(&chip->battemp_work, HZ*1); +#endif +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + chip->is_charger_changed_from_irq = true; + + if(wake_lock_active(&chip->lcs_wake_lock)) + wake_unlock(&chip->lcs_wake_lock); +#endif if (!usb_present) { +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + if (usb_chg_state == IS_OPEN_TA || chg_state == VZW_NOT_CHARGING || chg_state == VZW_USB_DRIVER_UNINSTALLED) { + lge_usb_config_finish = 0; + usb_chg_state = IS_USB_DRIVER_UNINSTALLED; + chg_state = VZW_NO_CHARGER; + vzw_chg_present = NOT_PRESENT; +#ifdef CONFIG_LGE_PM_VZW_LLK + temp_state = 0; +#endif + power_supply_changed(&chip->batt_psy); + } + /* input_current_check_work should be cancelled if charger is removed before checking input current by AICL */ + schedule_work(&chip->cancel_input_current_check_work); +#endif /* when a valid charger inserted, and increase the * charger voltage to OVP threshold, then * usb_in_valid falling edge interrupt triggers. @@ -1479,7 +1841,6 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) chip->chg_done = false; } qpnp_chg_usb_suspend_enable(chip, 0); - qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); chip->prev_usb_max_ma = -EINVAL; chip->aicl_settled = false; } else { @@ -1510,7 +1871,23 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) } schedule_delayed_work(&chip->eoc_work, msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START schedule_work(&chip->soc_check_work); +#endif + +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + schedule_work(&chip->resume_check_work); + + //pr_err("entered qpnp_chg_usb_usbin_valid_irq_handler\n"); +#endif + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + if (lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO) + schedule_delayed_work(&chip->input_current_check_work, HZ*30); + else + schedule_delayed_work(&chip->input_current_check_work, HZ*10); + chip->is_vzw_slow_charging = false; +#endif } power_supply_set_present(chip->usb_psy, chip->usb_present); @@ -1520,40 +1897,22 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip) return IRQ_HANDLED; } -#define TEST_EN_SMBC_LOOP 0xE5 -#define IBAT_REGULATION_DISABLE BIT(2) +/* */ +#ifndef CONFIG_LGE_PM static irqreturn_t qpnp_chg_bat_if_batt_temp_irq_handler(int irq, void *_chip) { struct qpnp_chg_chip *chip = _chip; - int batt_temp_good, rc; + int batt_temp_good; batt_temp_good = qpnp_chg_is_batt_temp_ok(chip); - pr_debug("batt-temp triggered: %d\n", batt_temp_good); - - rc = qpnp_chg_masked_write(chip, - chip->buck_base + SEC_ACCESS, - 0xFF, - 0xA5, 1); - if (rc) { - pr_err("failed to write SEC_ACCESS rc=%d\n", rc); - return rc; - } - - rc = qpnp_chg_masked_write(chip, - chip->buck_base + TEST_EN_SMBC_LOOP, - IBAT_REGULATION_DISABLE, - batt_temp_good ? 0 : IBAT_REGULATION_DISABLE, 1); - if (rc) { - pr_err("failed to write COMP_OVR1 rc=%d\n", rc); - return rc; - } + pr_err("batt-temp triggered: %d\n", batt_temp_good); pr_debug("psy changed batt_psy\n"); power_supply_changed(&chip->batt_psy); return IRQ_HANDLED; } - +#endif static irqreturn_t qpnp_chg_bat_if_batt_pres_irq_handler(int irq, void *_chip) { @@ -1561,7 +1920,7 @@ qpnp_chg_bat_if_batt_pres_irq_handler(int irq, void *_chip) int batt_present; batt_present = qpnp_chg_is_batt_present(chip); - pr_debug("batt-pres triggered: %d\n", batt_present); + pr_err("batt-pres triggered: %d\n", batt_present); if (chip->batt_present ^ batt_present) { if (batt_present) { @@ -1596,8 +1955,13 @@ qpnp_chg_dc_dcin_valid_irq_handler(int irq, void *_chip) struct qpnp_chg_chip *chip = _chip; int dc_present; +#ifdef CONFIG_LGE_PM + if ( (chip->uevent_wake_lock.ws.name) != NULL ) + wake_lock_timeout(&chip->uevent_wake_lock, HZ*2); +#endif + dc_present = qpnp_chg_is_dc_chg_plugged_in(chip); - pr_debug("dcin-valid triggered: %d\n", dc_present); + pr_err("dcin-valid triggered: %d\n", dc_present); if (chip->dc_present ^ dc_present) { chip->dc_present = dc_present; @@ -1614,7 +1978,9 @@ qpnp_chg_dc_dcin_valid_irq_handler(int irq, void *_chip) } schedule_delayed_work(&chip->eoc_work, msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START schedule_work(&chip->soc_check_work); +#endif } pr_debug("psy changed dc_psy\n"); power_supply_changed(&chip->dc_psy); @@ -1633,7 +1999,24 @@ qpnp_chg_chgr_chg_failed_irq_handler(int irq, void *_chip) struct qpnp_chg_chip *chip = _chip; int rc; - pr_debug("chg_failed triggered\n"); + pr_err("chg_failed triggered\n"); +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + if(chip->from_temp_monitor_vbat_det_high == true) + { + qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv - chip->resume_delta_mv); + chip->from_temp_monitor_vbat_det_high = false; + } +#endif + +// For the CHG Stop +#ifdef CONFIG_LGE_PM + if (!pseudo_batt_info.mode){ + pr_info("=========== [CHG STOP] ==============\n"); + + chip->chg_fail_irq_happen = true; + qpnp_chg_charge_en(chip, 0); + } +#endif rc = qpnp_chg_masked_write(chip, chip->chgr_base + CHGR_CHG_FAILED, @@ -1660,8 +2043,14 @@ qpnp_chg_chgr_chg_trklchg_irq_handler(int irq, void *_chip) { struct qpnp_chg_chip *chip = _chip; - pr_debug("TRKL IRQ triggered\n"); - + pr_err("TRKL IRQ triggered\n"); +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + if(chip->from_temp_monitor_vbat_det_high == true) + { + qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv - chip->resume_delta_mv); + chip->from_temp_monitor_vbat_det_high = false; + } +#endif chip->chg_done = false; if (chip->bat_if_base) { pr_debug("psy changed batt_psy\n"); @@ -1682,7 +2071,7 @@ qpnp_chg_chgr_chg_fastchg_irq_handler(int irq, void *_chip) if (rc) pr_err("failed to read interrupt sts %d\n", rc); - pr_debug("FAST_CHG IRQ triggered\n"); + pr_err("FAST_CHG IRQ triggered\n"); chip->chg_done = false; if (chip->bat_if_base) { pr_debug("psy changed batt_psy\n"); @@ -1708,11 +2097,37 @@ qpnp_chg_chgr_chg_fastchg_irq_handler(int irq, void *_chip) pm_stay_awake(chip->dev); } +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + if(chip->from_temp_monitor_vbat_det_high == true) + { + qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv - chip->resume_delta_mv); + chip->from_temp_monitor_vbat_det_high = false; + } +#endif + qpnp_chg_enable_irq(&chip->chg_vbatdet_lo); + if (chgr_sts & FAST_CHG_ON_IRQ) { +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP + pr_debug("======= [FAST CHG IRQ] DCP = %d, SDP = %d ========\n", chip->ac_online, qpnp_chg_is_usb_chg_plugged_in(chip)); + // DCP Cable + if (chip->ac_online && qpnp_chg_is_usb_chg_plugged_in(chip)) + { + pr_debug("======= [FAST CHG IRQ] DCP CABLE - Ext. OVP is HIGH =============\n"); + lge_set_chg_path_to_external(); + } + // SDP Cable + else if (!chip->ac_online && qpnp_chg_is_usb_chg_plugged_in(chip)) + { + pr_debug("======= [FAST CHG IRQ] SDP CABLE - Ext. OVP is LOW =============\n"); + lge_set_chg_path_to_internal(); + } +#endif + } return IRQ_HANDLED; } +#ifndef CONFIG_LGE_PM static int qpnp_dc_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) @@ -1726,6 +2141,7 @@ qpnp_dc_property_is_writeable(struct power_supply *psy, return 0; } +#endif static int qpnp_batt_property_is_writeable(struct power_supply *psy, @@ -1733,13 +2149,17 @@ qpnp_batt_property_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_CHARGING_ENABLED: +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: +#endif case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: case POWER_SUPPLY_PROP_INPUT_CURRENT_TRIM: case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: case POWER_SUPPLY_PROP_VOLTAGE_MIN: +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: +#endif case POWER_SUPPLY_PROP_CAPACITY: return 1; default: @@ -1749,6 +2169,7 @@ qpnp_batt_property_is_writeable(struct power_supply *psy, return 0; } +#ifndef CONFIG_LGE_PM static int qpnp_chg_buck_control(struct qpnp_chg_chip *chip, int enable) { @@ -1771,6 +2192,7 @@ qpnp_chg_buck_control(struct qpnp_chg_chip *chip, int enable) return rc; } +#endif static int switch_usb_to_charge_mode(struct qpnp_chg_chip *chip) @@ -1857,9 +2279,24 @@ static enum power_supply_property msm_batt_power_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_TEMP, +#ifndef CONFIG_LGE_PM POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, +#endif +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + POWER_SUPPLY_PROP_PSEUDO_BATT, +#endif +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + POWER_SUPPLY_PROP_VALID_BATT, +#endif +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + POWER_SUPPLY_PROP_VZW_CHG_STATE, +#endif +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + POWER_SUPPLY_PROP_HW_REV, + POWER_SUPPLY_PROP_USB_ID, +#endif POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_OCV, }; @@ -1872,6 +2309,32 @@ static char *pm_batt_supplied_to[] = { "bms", }; + +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +int chg_status_set(int value) +{ + struct qpnp_chg_chip *chip = qpnp_chg; + + + if (value == 0) { + /* stop charging */ + pr_info("[kernel] stop charging start\n"); + qpnp_chg_charge_en(chip, 0); + testmode_stop_eoc = 1; + + } else if (value == 1) { + /* start charging */ + pr_info("[kernel] start charging start\n"); + qpnp_chg_charge_en(chip, 1); + } + + power_supply_changed(&chip->batt_psy); + + return 0; +} + +EXPORT_SYMBOL(chg_status_set); +#endif static int charger_monitor; module_param(charger_monitor, int, 0644); @@ -1894,17 +2357,55 @@ qpnp_power_get_property_mains(struct power_supply *psy, val->intval = 0; if (chip->charging_disabled) return 0; - +#ifdef CONFIG_LGE_PM +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + if (chg_state == VZW_NOT_CHARGING){ + val->intval = 1; + } + else +#endif + val->intval = chip->ac_online; +#else // Qualcomm Original val->intval = qpnp_chg_is_dc_chg_plugged_in(chip); +#endif + break; +#ifdef CONFIG_LGE_PM + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = chip->current_max; + break; +#endif + default: + return -EINVAL; + } + return 0; +} +#ifdef CONFIG_LGE_PM +static int +qpnp_power_set_property_mains(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct qpnp_chg_chip *chip = container_of(psy, struct qpnp_chg_chip, + dc_psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + chip->ac_online = val->intval; break; case POWER_SUPPLY_PROP_CURRENT_MAX: - val->intval = chip->maxinput_dc_ma * 1000; + chip->current_max = val->intval; break; default: return -EINVAL; } + + pr_debug("[LGE] %s cur_max[%d], online[%d]\n", + __func__, chip->current_max, chip->ac_online); + power_supply_changed(&chip->dc_psy); return 0; } +#endif static void qpnp_aicl_check_work(struct work_struct *work) @@ -1913,19 +2414,55 @@ qpnp_aicl_check_work(struct work_struct *work) struct qpnp_chg_chip *chip = container_of(dwork, struct qpnp_chg_chip, aicl_check_work); union power_supply_propval ret = {0,}; +#ifdef CONFIG_LGE_PM + static int aicl_check_count=0; + static bool aicl_check_wake_lock = false; +#endif + +#ifdef CONFIG_LGE_PM + if(aicl_check_wake_lock == true) + { + pm_relax(chip->dev); + pr_info("[LGE_AICL] aicl_pm_relax , aicl_check_wake_lock = %d\n",aicl_check_wake_lock); + aicl_check_wake_lock = false; + } + + if(!charger_monitor) + { + if( aicl_check_count<6) //60 seconds at 10 second intervals + goto aicl_check_retry; + } +#endif if (!charger_monitor && qpnp_chg_is_usb_chg_plugged_in(chip)) { chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &ret); if ((ret.intval / 1000) > USB_WALL_THRESHOLD_MA) { - pr_debug("no charger_monitor present set iusbmax %d\n", + pr_info("[LGE_AICL]no charger_monitor present set iusbmax %d\n", ret.intval / 1000); qpnp_chg_iusbmax_set(chip, ret.intval / 1000); } } else { - pr_debug("charger_monitor is present\n"); +#ifdef CONFIG_LGE_PM + aicl_check_count=0; + pr_info("[LGE_AICL] aicl_check_count set to ZERO = %d\n", aicl_check_count); +#endif + pr_info("[LGE_AICL]charger_monitor is present\n"); } chip->charger_monitor_checked = true; + +#ifdef CONFIG_LGE_PM + return; +#endif + +#ifdef CONFIG_LGE_PM +aicl_check_retry: + aicl_check_count++; + pr_info("[LGE_AICL] aicl_check_count = %d\n", aicl_check_count); + pm_stay_awake(chip->dev); + aicl_check_wake_lock = true; + schedule_delayed_work(&chip->aicl_check_work,msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); +#endif } static int @@ -1934,7 +2471,11 @@ get_prop_battery_voltage_now(struct qpnp_chg_chip *chip) int rc = 0; struct qpnp_vadc_result results; +#ifdef CONFIG_LGE_PM + if (chip->revision < 0 && chip->type == SMBB) { +#else if (chip->revision == 0 && chip->type == SMBB) { +#endif pr_err("vbat reading not supported for 1.0 rc=%d\n", rc); return 0; } else { @@ -1965,9 +2506,32 @@ get_prop_batt_present(struct qpnp_chg_chip *chip) #define BATT_TEMP_HOT BIT(6) #define BATT_TEMP_OK BIT(7) + +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO +#define BATT_TEMP_OVERHEAT 550 +#define BATT_TEMP_COLD (-100) +#endif static int get_prop_batt_health(struct qpnp_chg_chip *chip) { +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + if (chip->usb_present){ + if (chip->btm_state == BTM_HEALTH_OVERHEAT) + return POWER_SUPPLY_HEALTH_OVERHEAT; + if (chip->btm_state == BTM_HEALTH_COLD) + return POWER_SUPPLY_HEALTH_COLD; + else + return POWER_SUPPLY_HEALTH_GOOD; + } + else { + if (chip->current_batt_temp > BATT_TEMP_OVERHEAT) + return POWER_SUPPLY_HEALTH_OVERHEAT; + if (chip->current_batt_temp < BATT_TEMP_COLD) + return POWER_SUPPLY_HEALTH_COLD; + else + return POWER_SUPPLY_HEALTH_GOOD; + } +#else u8 batt_health; int rc; @@ -1984,6 +2548,7 @@ get_prop_batt_health(struct qpnp_chg_chip *chip) return POWER_SUPPLY_HEALTH_OVERHEAT; else return POWER_SUPPLY_HEALTH_COLD; +#endif } static int @@ -2010,29 +2575,12 @@ get_prop_charge_type(struct qpnp_chg_chip *chip) return POWER_SUPPLY_CHARGE_TYPE_NONE; } -#define DEFAULT_CAPACITY 50 -static int -get_batt_capacity(struct qpnp_chg_chip *chip) -{ - union power_supply_propval ret = {0,}; - - if (chip->fake_battery_soc >= 0) - return chip->fake_battery_soc; - if (chip->use_default_batt_values || !get_prop_batt_present(chip)) - return DEFAULT_CAPACITY; - if (chip->bms_psy) { - chip->bms_psy->get_property(chip->bms_psy, - POWER_SUPPLY_PROP_CAPACITY, &ret); - return ret.intval; - } - return DEFAULT_CAPACITY; -} - static int get_prop_batt_status(struct qpnp_chg_chip *chip) { int rc; u8 chgr_sts, bat_if_sts; + int chg_type = get_prop_charge_type(chip); if ((qpnp_chg_is_usb_chg_plugged_in(chip) || qpnp_chg_is_dc_chg_plugged_in(chip)) && chip->chg_done) { @@ -2050,19 +2598,36 @@ get_prop_batt_status(struct qpnp_chg_chip *chip) pr_err("failed to read bat_if sts %d\n", rc); return POWER_SUPPLY_CHARGE_TYPE_NONE; } +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + if (qpnp_chg_is_usb_chg_plugged_in(chip)){ + if (chip->pseudo_ui_chg) + return POWER_SUPPLY_STATUS_CHARGING; + else if (chip->not_chg == CHG_BATT_STPCHG_STATE) + return POWER_SUPPLY_STATUS_NOT_CHARGING; + } +#endif + if (chg_type == POWER_SUPPLY_CHARGE_TYPE_UNKNOWN || + chg_type == POWER_SUPPLY_CHARGE_TYPE_NONE) { +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + if ((chip->usb_present) || (chg_state == VZW_NOT_CHARGING) + || (chg_state == VZW_USB_DRIVER_UNINSTALLED)){ +#else + if (chip->usb_present){ + +#endif + return POWER_SUPPLY_STATUS_NOT_CHARGING; + } + else { + return POWER_SUPPLY_STATUS_DISCHARGING; + } + + } - if ((chgr_sts & TRKL_CHG_ON_IRQ) && !(bat_if_sts & BAT_FET_ON_IRQ)) + if (chgr_sts & TRKL_CHG_ON_IRQ && bat_if_sts & BAT_FET_ON_IRQ) return POWER_SUPPLY_STATUS_CHARGING; if (chgr_sts & FAST_CHG_ON_IRQ && bat_if_sts & BAT_FET_ON_IRQ) return POWER_SUPPLY_STATUS_CHARGING; - /* report full if state of charge is 100 and a charger is connected */ - if ((qpnp_chg_is_usb_chg_plugged_in(chip) || - qpnp_chg_is_dc_chg_plugged_in(chip)) - && get_batt_capacity(chip) == 100) { - return POWER_SUPPLY_STATUS_FULL; - } - return POWER_SUPPLY_STATUS_DISCHARGING; } @@ -2114,11 +2679,15 @@ get_prop_charge_full(struct qpnp_chg_chip *chip) return 0; } +#define DEFAULT_CAPACITY 50 static int get_prop_capacity(struct qpnp_chg_chip *chip) { union power_supply_propval ret = {0,}; - int battery_status, bms_status, soc, charger_in; + int soc; +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START + int battery_status, bms_status, charger_in; +#endif if (chip->fake_battery_soc >= 0) return chip->fake_battery_soc; @@ -2130,6 +2699,7 @@ get_prop_capacity(struct qpnp_chg_chip *chip) chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); soc = ret.intval; +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START battery_status = get_prop_batt_status(chip); chip->bms_psy->get_property(chip->bms_psy, POWER_SUPPLY_PROP_STATUS, &ret); @@ -2151,6 +2721,7 @@ get_prop_capacity(struct qpnp_chg_chip *chip) qpnp_chg_set_appropriate_vbatdet(chip); qpnp_chg_charge_en(chip, !chip->charging_disabled); } +#endif if (soc == 0) { if (!qpnp_chg_is_usb_chg_plugged_in(chip) && !qpnp_chg_is_usb_chg_plugged_in(chip)) @@ -2173,7 +2744,13 @@ get_prop_batt_temp(struct qpnp_chg_chip *chip) { int rc = 0; struct qpnp_vadc_result results; - + +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + if (pseudo_batt_info.mode) { + pr_debug("battery fake mode : %d \n", pseudo_batt_info.mode); + return pseudo_batt_info.temp * 10; + } +#endif if (chip->use_default_batt_values || !get_prop_batt_present(chip)) return DEFAULT_TEMP; @@ -2198,6 +2775,26 @@ static int get_prop_cycle_count(struct qpnp_chg_chip *chip) return ret.intval; } +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER +static int +get_prop_batt_id_valid(void) +{ + return (int)is_lge_battery_valid(); +} +#endif +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE +static int +get_prop_hw_rev(void) +{ + return lge_get_board_revno(); +} + +static int +get_prop_usb_id(void) +{ + return lge_pm_get_cable_type(); +} +#endif static int get_prop_vchg_loop(struct qpnp_chg_chip *chip) { u8 buck_sts; @@ -2220,6 +2817,189 @@ static int get_prop_online(struct qpnp_chg_chip *chip) return qpnp_chg_is_batfet_closed(chip); } +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START +static void +qpnp_chg_resume_check_work(struct work_struct *work) +{ + struct qpnp_chg_chip *chip = container_of(work, + struct qpnp_chg_chip, resume_check_work); + + int battery_charging_status,charger_inserted ; + battery_charging_status = get_prop_batt_status(chip); + + charger_inserted = qpnp_chg_is_usb_chg_plugged_in(chip); + pr_err("qpnp_chg_resume_check_work, chagerin=%d, batt_status=%d\n", charger_inserted,battery_charging_status); + if((battery_charging_status != POWER_SUPPLY_STATUS_CHARGING) + && charger_inserted) + { + //pr_err("start charing in monitor temp, vbatt=%d\n", req.batt_volt); + qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv + chip->resume_delta_mv); + qpnp_chg_charge_en(chip, !chip->charging_disabled); + //chip->from_usbin_valid_irq = false; + chip->from_temp_monitor_vbat_det_high = true; + } +} +#endif + + +#ifdef CONFIG_LGE_PM_VZW_LLK +int32_t vzw_llk_enable_charging(bool enable) +{ + int ret ; + + pr_err("enable=%d.\n", enable); + + if (enable) { + schedule_delayed_work(&qpnp_chg->battemp_work,HZ*5); + schedule_delayed_work(&qpnp_chg->eoc_work, + msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); + } + else { + cancel_delayed_work_sync(&qpnp_chg->battemp_work); + cancel_delayed_work_sync(&qpnp_chg->eoc_work); + pm_relax(qpnp_chg->dev); + } + ret = qpnp_chg_charge_en(qpnp_chg, enable); + + if (ret) { + pr_err("Failed to set CHG_ENABLE_BIT rc=%d\n", ret); + return ret; + } + + if (!enable && !(chg_state == VZW_LLK_NOT_CHARGING)) { + temp_state = chg_state; + chg_state = VZW_LLK_NOT_CHARGING; + pr_info("CHG_STATE : %d\n", chg_state); + } else if ((chg_state == VZW_LLK_NOT_CHARGING) && enable){ + chg_state = temp_state; + pr_info("CHG_STATE : %d\n", chg_state); + } + + power_supply_changed(&qpnp_chg->batt_psy); + + return 0; +} +#endif + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG +extern int lge_usb_config_finish; +extern void send_drv_state_uevent(int usb_drv_state); + +void set_vzw_usb_charging_state(int state) +{ + struct qpnp_chg_chip *chip = qpnp_chg; + usb_chg_state = state; + + switch (state) { + case IS_OPEN_TA: + cancel_delayed_work_sync(&chip->battemp_work); + cancel_delayed_work_sync(&chip->eoc_work); + pm_relax(chip->dev); + qpnp_chg_charge_en(chip, 0); + chg_state = VZW_NOT_CHARGING; + pr_info("%s : OPEN TA is connected!!\n", __func__); + break; + case IS_USB_DRIVER_UNINSTALLED: + qpnp_chg_charge_en(chip, 0); + send_drv_state_uevent(0); + chg_state = VZW_USB_DRIVER_UNINSTALLED; + pr_info("[USB_DRV] USB DRIVER UNINSTALLED !!\n"); + break; + case IS_USB_DRIVER_INSTALLED: + send_drv_state_uevent(1); + chg_state = VZW_NORMAL_CHARGING; + pr_info("[USB_DRV] USB DRIVER INSTALLED !!\n"); + break; + } + + power_supply_changed(&chip->batt_psy); +} +EXPORT_SYMBOL(set_vzw_usb_charging_state); + +int get_vzw_usb_charging_state(void) +{ + return usb_chg_state; +} +EXPORT_SYMBOL(get_vzw_usb_charging_state); + +static void vzw_fast_chg_change_usb_charging_state(struct qpnp_chg_chip *chip) +{ + struct usb_phy *otg_xceiv; + struct msm_otg *motg; + + otg_xceiv = usb_get_transceiver(); + if (!otg_xceiv) { + pr_err("Failed to get usb transceiver.\n"); + return; + } + + motg = container_of(otg_xceiv, struct msm_otg, phy); + if (!motg) { + pr_err("Failed to get otg driver data.\n"); + return; + } + + if(lge_usb_config_finish == 0) { + qpnp_chg_charge_en(chip, 0); + pr_info("USB cable is connected, but USB is not configured!\n"); + } else if (usb_chg_state == IS_USB_DRIVER_INSTALLED) { + qpnp_chg_charge_en(chip, !chip->charging_disabled); + pr_info("USB is configured and USB Driver is installed!\n"); + usb_chg_state = IS_USB_CHARGING_ENABLE; + } +} + +static void vzw_fast_chg_set_charging(struct qpnp_chg_chip *chip) +{ + if (vzw_chg_present == NOT_PRESENT) { + if (chip->is_vzw_slow_charging) { + vzw_chg_present = SLOW_PRESENT; + pr_info("slow TA, decrease charging current.\n"); + } else { + vzw_chg_present = UNKNOWN_PRESENT; + } + } else if (vzw_chg_present == SLOW_PRESENT){ + chg_state = VZW_UNDER_CURRENT_CHARGING; + pr_info("chg_state 1 = %d\n", chg_state); + } else { + chg_state = VZW_NORMAL_CHARGING; + pr_info("chg_state 2 = %d\n", chg_state); + } + power_supply_changed(&chip->batt_psy); +} + +static void vzw_fast_chg_input_current_check_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct qpnp_chg_chip *chip = container_of(dwork, + struct qpnp_chg_chip, input_current_check_work); + int ret, threshold = 500; + + ret = qpnp_chg_usb_iusbmax_get(chip); + if (ret <= threshold && (lge_power_supply_get_type() == POWER_SUPPLY_TYPE_USB_DCP)){ + pr_info("Slow charger is detected , input_current_max = %d\n", ret); + chip->is_vzw_slow_charging = true; + vzw_chg_present = SLOW_PRESENT; + } + else { + pr_info("Normal charger is detected , input_current_max = %d\n", ret); + chip->is_vzw_slow_charging = false; + } + + + if (lge_power_supply_get_type() == POWER_SUPPLY_TYPE_USB_DCP) + vzw_fast_chg_set_charging(chip); +} + +static void vzw_fast_chg_cancel_input_current_check_work(struct work_struct *work) +{ + struct qpnp_chg_chip *chip = container_of(work, + struct qpnp_chg_chip, cancel_input_current_check_work); + + cancel_delayed_work_sync(&chip->input_current_check_work); +} +#endif + static void qpnp_batt_external_power_changed(struct power_supply *psy) { @@ -2233,10 +3013,70 @@ qpnp_batt_external_power_changed(struct power_supply *psy) chip->usb_psy->get_property(chip->usb_psy, POWER_SUPPLY_PROP_ONLINE, &ret); + +/* */ +#ifdef CONFIG_LGE_PM + if(is_factory_cable()){ + qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MAX_MA); + qpnp_chg_usb_suspend_enable(chip, 0); + power_supply_changed(&chip->batt_psy); + return; + } +#endif +/* */ +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + if((pseudo_batt_info.mode == 1)&&(!is_factory_cable())){ + qpnp_chg_iusbmax_set(chip, PSEUDO_BATT_MAX); + qpnp_chg_usb_suspend_enable(chip, 0); + goto skip_set_iusb_max; + } +#endif + + #ifdef CONFIG_LGE_PM + pr_info("[LGE] usb_online: %d ac_online: %d\n", ret.intval, chip->ac_online); + pr_info("[LGE] power_supply_type = %d\n",lge_power_supply_get_type()); + #endif + /* Only honour requests while USB is present */ if (qpnp_chg_is_usb_chg_plugged_in(chip)) { +#ifdef CONFIG_LGE_PM + if(lge_power_supply_get_type() == POWER_SUPPLY_TYPE_USB_DCP) {// DCP + chip->dc_psy.get_property(&chip->dc_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &ret); +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + vzw_fast_chg_set_charging(chip); + } else if (ret.intval) { +#else + } else { //others +#endif + chip->usb_psy->get_property(chip->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &ret); +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + vzw_chg_present = USB_PRESENT; + if (usb_chg_state != IS_USB_CHARGING_ENABLE) { + qpnp_chg_charge_en(chip, 0); + vzw_fast_chg_change_usb_charging_state(chip); + if (usb_chg_state == IS_USB_CHARGING_ENABLE) { + schedule_work(&chip->resume_check_work); + } + } + } else { //others + chip->usb_psy->get_property(chip->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &ret); +#endif + } +#else chip->usb_psy->get_property(chip->usb_psy, - POWER_SUPPLY_PROP_CURRENT_MAX, &ret); + POWER_SUPPLY_PROP_CURRENT_MAX, &ret); +#endif +#ifdef CONFIG_LGE_PM_VZW_LLK + if (get_prop_capacity(chip) > 34) { + qpnp_chg_charge_en(chip, 0); + temp_state = chg_state; + chg_state = VZW_LLK_NOT_CHARGING; + pr_info("VZW LLK Charging Stop!!, CHG_STATE : %d\n", chg_state); + } +#endif if (chip->prev_usb_max_ma == ret.intval) goto skip_set_iusb_max; @@ -2245,9 +3085,18 @@ qpnp_batt_external_power_changed(struct power_supply *psy) if (ret.intval <= 2 && !chip->use_default_batt_values && get_prop_batt_present(chip)) { +#ifdef CONFIG_LGE_PM //work-around code for prevent current setting zero when aconline & usbonline both zero on cable detect state + if ( ret.intval != 0) + { + qpnp_chg_usb_suspend_enable(chip, 1); + qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); + pr_err("[LGE] current zero set usb_online: %d ac_online: %d\n", ret.intval, chip->ac_online); + } +#else if (ret.intval == 2) qpnp_chg_usb_suspend_enable(chip, 1); qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); +#endif } else { qpnp_chg_usb_suspend_enable(chip, 0); if (((ret.intval / 1000) > USB_WALL_THRESHOLD_MA) @@ -2274,6 +3123,19 @@ qpnp_batt_external_power_changed(struct power_supply *psy) } } } + else { + qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100); + qpnp_chg_usb_suspend_enable(chip, 1); +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + lge_usb_config_finish = 0; + usb_chg_state = IS_USB_DRIVER_UNINSTALLED; + chg_state = VZW_NO_CHARGER; + vzw_chg_present = NOT_PRESENT; +#ifdef CONFIG_LGE_PM_VZW_LLK + temp_state = 0; +#endif +#endif + } skip_set_iusb_max: pr_debug("end of power supply changed\n"); @@ -2319,14 +3181,31 @@ qpnp_batt_power_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_TEMP: val->intval = get_prop_batt_temp(chip); +#ifdef CONFIG_LGE_PM_VZW_HIGH_TEMP_PWR_OFF + if(lge_get_boot_mode() != LGE_BOOT_MODE_CHARGERLOGO) { + if(val->intval > 600) + write_high_temp_power_off("/persist/is_high_temp_pwr_off"); + } +#endif +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + chip->current_batt_temp = val->intval; +#endif break; +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_COOL_TEMP: val->intval = chip->cool_bat_decidegc; break; case POWER_SUPPLY_PROP_WARM_TEMP: val->intval = chip->warm_bat_decidegc; break; +#endif case POWER_SUPPLY_PROP_CAPACITY: +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + if (pseudo_batt_info.mode) { + val->intval = pseudo_batt_info.capacity; + break; + } +#endif val->intval = get_prop_capacity(chip); break; case POWER_SUPPLY_PROP_CURRENT_NOW: @@ -2341,9 +3220,34 @@ qpnp_batt_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGING_ENABLED: val->intval = !(chip->charging_disabled); break; +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: val->intval = chip->therm_lvl_sel; break; +#endif +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + case POWER_SUPPLY_PROP_PSEUDO_BATT: + val->intval = pseudo_batt_info.mode; + break; +#endif +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + case POWER_SUPPLY_PROP_VALID_BATT: + val->intval = get_prop_batt_id_valid(); + break; +#endif +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + case POWER_SUPPLY_PROP_VZW_CHG_STATE: + val->intval = chg_state; + break; +#endif +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + case POWER_SUPPLY_PROP_HW_REV: + val->intval = get_prop_hw_rev(); + break; + case POWER_SUPPLY_PROP_USB_ID: + val->intval = get_prop_usb_id(); + break; +#endif case POWER_SUPPLY_PROP_CYCLE_COUNT: val->intval = get_prop_cycle_count(chip); break; @@ -2396,14 +3300,58 @@ qpnp_chg_bat_if_configure_btc(struct qpnp_chg_chip *chip) mask |= BTC_COLD; } +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + + if (pseudo_batt_info.mode) { // pseudo == 1 , btc is disable. + mask |= BTC_CONFIG_ENABLED; + } else { // pseudo ==0, btc is enable. + mask |= BTC_CONFIG_ENABLED; + btc_cfg |= BTC_CONFIG_ENABLED; + } + +#else + if (chip->btc_disabled) mask |= BTC_CONFIG_ENABLED; + +#endif return qpnp_chg_masked_write(chip, chip->bat_if_base + BAT_IF_BTC_CTRL, mask, btc_cfg, 1); } +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY +int pseudo_batt_set(struct pseudo_batt_info_type *info) +{ + int rc = 0; + struct qpnp_chg_chip *chip = qpnp_chg; + pr_err("pseudo_batt_set\n"); + pseudo_batt_info.mode = info->mode; + pseudo_batt_info.id = info->id; + pseudo_batt_info.therm = info->therm; + pseudo_batt_info.temp = info->temp; + pseudo_batt_info.volt = info->volt; + pseudo_batt_info.capacity = info->capacity; + pseudo_batt_info.charging = info->charging; + + if((pseudo_batt_info.mode == 1)&&(!is_factory_cable())){ + pr_info("Pseudo battery mode, set IUSB to PSEUDO_BATT_MAX.\n"); + qpnp_chg_iusbmax_set(chip, PSEUDO_BATT_MAX); + } + + rc = qpnp_chg_bat_if_configure_btc(chip); + if (rc) { + pr_err("failed to configure btc %d\n", rc); + } + + power_supply_changed(&chip->batt_psy); + + return 0; +} +EXPORT_SYMBOL(pseudo_batt_set); +#endif + #define QPNP_CHG_IBATSAFE_MIN_MA 100 #define QPNP_CHG_IBATSAFE_MAX_MA 3250 #define QPNP_CHG_I_STEP_MA 50 @@ -2731,6 +3679,7 @@ qpnp_boost_vget_uv(struct qpnp_chg_chip *chip) return BOOST_MIN_UV + ((boost_reg - BOOST_MIN) * BOOST_STEP_UV); } +#ifndef CONFIG_LGE_PM static void qpnp_chg_set_appropriate_battery_current(struct qpnp_chg_chip *chip) { @@ -2766,6 +3715,7 @@ qpnp_batt_system_temp_level_set(struct qpnp_chg_chip *chip, int lvl_sel) pr_err("Unsupported level selected %d\n", lvl_sel); } } +#endif /* OTG regulator operations */ static int @@ -3097,6 +4047,12 @@ qpnp_chg_adjust_vddmax(struct qpnp_chg_chip *chip, int vbat_mv) qpnp_chg_set_appropriate_vddmax(chip); } +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO +bool is_eoc_work_stop = false; +extern bool is_enter_first; +#endif + + #define CONSECUTIVE_COUNT 3 #define VBATDET_MAX_ERR_MV 50 static void @@ -3111,6 +4067,23 @@ qpnp_eoc_work(struct work_struct *work) u8 batt_sts = 0, buck_sts = 0, chg_sts = 0; bool vbat_lower_than_vbatdet; +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + is_eoc_work_stop = false; +#endif + + +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + if (testmode_stop_eoc == 1){ + testmode_stop_eoc = 0; + goto stop_eoc; + } +#endif + +#ifdef CONFIG_LGE_PM + if(chip->chg_fail_irq_happen) + goto stop_eoc; +#endif + pm_stay_awake(chip->dev); qpnp_chg_charge_en(chip, !chip->charging_disabled); @@ -3152,17 +4125,24 @@ qpnp_eoc_work(struct work_struct *work) vbat_lower_than_vbatdet = !(chg_sts & VBAT_DET_LOW_IRQ); if (vbat_lower_than_vbatdet && vbat_mv < (chip->max_voltage_mv - chip->resume_delta_mv - - chip->vbatdet_max_err_mv)) { + - VBATDET_MAX_ERR_MV)) { vbat_low_count++; pr_debug("woke up too early vbat_mv = %d, max_mv = %d, resume_mv = %d tolerance_mv = %d low_count = %d\n", vbat_mv, chip->max_voltage_mv, chip->resume_delta_mv, - chip->vbatdet_max_err_mv, - vbat_low_count); + VBATDET_MAX_ERR_MV, vbat_low_count); if (vbat_low_count >= CONSECUTIVE_COUNT) { pr_debug("woke up too early stopping\n"); qpnp_chg_enable_irq(&chip->chg_vbatdet_lo); +#ifdef CONFIG_LGE_PM +// This code is given by Qualcomm. +// During CC charging , the device does not go to power collapse by this code. + vbat_low_count=0; + count=0; + goto check_again_later; +#else goto stop_eoc; +#endif } else { goto check_again_later; } @@ -3181,6 +4161,17 @@ qpnp_eoc_work(struct work_struct *work) count = 0; } else if (ibat_ma > 0) { pr_debug("Charging but system demand increased\n"); +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + /* with 4.35V battery(full charging) ->insert charger(TA)->EOC work is working + but, the ibatt current is + values. after this, the phone doesn't start resume charging + when the vbatt goes to resume voltage*/ + if(chip->from_temp_monitor_vbat_det_high == true) + { + //pr_err("Charging but system demand increased\n"); + qpnp_chg_vbatdet_set(chip, chip->max_voltage_mv - chip->resume_delta_mv); + chip->from_temp_monitor_vbat_det_high = false; + } +#endif count = 0; } else { if (count == CONSECUTIVE_COUNT) { @@ -3222,7 +4213,18 @@ qpnp_eoc_work(struct work_struct *work) stop_eoc: vbat_low_count = 0; count = 0; - pm_relax(chip->dev); + +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + is_eoc_work_stop = true; + + if(lge_get_boot_mode() != LGE_BOOT_MODE_CHARGERLOGO) + { + pr_info("===== [qpnp_eoc_work] PM RELAX !!! ======\n"); + pm_relax(chip->dev); + } +#else + pm_relax(chip->dev); +#endif } static void @@ -3251,6 +4253,7 @@ qpnp_chg_insertion_ocv_work(struct work_struct *work) power_supply_changed(&chip->batt_psy); } +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START static void qpnp_chg_soc_check_work(struct work_struct *work) { @@ -3259,6 +4262,7 @@ qpnp_chg_soc_check_work(struct work_struct *work) get_prop_capacity(chip); } +#endif #define HYSTERISIS_DECIDEGC 20 static void @@ -3279,7 +4283,13 @@ qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx) state == ADC_TM_WARM_STATE ? "warm" : "cool"); if (state == ADC_TM_WARM_STATE) { +/* + */ +#ifdef CONFIG_LGE_PM + if (temp >= chip->warm_bat_decidegc) { +#else if (temp > chip->warm_bat_decidegc) { +#endif /* Normal to warm */ bat_warm = true; bat_cool = false; @@ -3299,7 +4309,12 @@ qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx) ADC_TM_HIGH_LOW_THR_ENABLE; } } else { +#ifdef CONFIG_LGE_PM + if (temp <= chip->cool_bat_decidegc) { +#else if (temp < chip->cool_bat_decidegc) { +#endif +/* */ /* Normal to cool */ bat_warm = false; bat_cool = true; @@ -3335,7 +4350,9 @@ qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx) * determine resume of charging. */ qpnp_chg_set_appropriate_vddmax(chip); +#ifndef CONFIG_LGE_PM qpnp_chg_set_appropriate_battery_current(chip); +#endif qpnp_chg_set_appropriate_vbatdet(chip); } @@ -3347,6 +4364,7 @@ qpnp_chg_adc_notification(enum qpnp_tm_state state, void *ctx) pr_err("request ADC error\n"); } +#ifndef CONFIG_LGE_PM #define MIN_COOL_TEMP -300 #define MAX_WARM_TEMP 1000 @@ -3408,6 +4426,7 @@ qpnp_chg_configure_jeita(struct qpnp_chg_chip *chip, mutex_unlock(&chip->jeita_configure_lock); return rc; } +#endif #define POWER_STAGE_REDUCE_CHECK_PERIOD_SECONDS 20 #define POWER_STAGE_REDUCE_MAX_VBAT_UV 3900000 @@ -3621,6 +4640,7 @@ qpnp_chg_reduce_power_stage_callback(struct alarm *alarm) schedule_work(&chip->reduce_power_stage_work); } +#ifndef CONFIG_LGE_PM static int qpnp_dc_power_set_property(struct power_supply *psy, enum power_supply_property psp, @@ -3651,6 +4671,7 @@ qpnp_dc_power_set_property(struct power_supply *psy, power_supply_changed(&chip->dc_psy); return rc; } +#endif// static int qpnp_batt_power_set_property(struct power_supply *psy, @@ -3662,12 +4683,14 @@ qpnp_batt_power_set_property(struct power_supply *psy, int rc = 0; switch (psp) { +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_COOL_TEMP: rc = qpnp_chg_configure_jeita(chip, psp, val->intval); break; case POWER_SUPPLY_PROP_WARM_TEMP: rc = qpnp_chg_configure_jeita(chip, psp, val->intval); break; +#endif case POWER_SUPPLY_PROP_CAPACITY: chip->fake_battery_soc = val->intval; power_supply_changed(&chip->batt_psy); @@ -3686,12 +4709,13 @@ qpnp_batt_power_set_property(struct power_supply *psy, qpnp_chg_charge_en(chip, !chip->charging_disabled); } break; +#ifndef CONFIG_LGE_PM case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: qpnp_batt_system_temp_level_set(chip, val->intval); break; +#endif case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX: - if (qpnp_chg_is_usb_chg_plugged_in(chip)) - qpnp_chg_iusbmax_set(chip, val->intval / 1000); + qpnp_chg_iusbmax_set(chip, val->intval / 1000); break; case POWER_SUPPLY_PROP_INPUT_CURRENT_TRIM: qpnp_chg_iusb_trim_set(chip, val->intval); @@ -3714,7 +4738,11 @@ qpnp_batt_power_set_property(struct power_supply *psy, static int qpnp_chg_setup_flags(struct qpnp_chg_chip *chip) { +#ifdef CONFIG_LGE_PM + if (chip->revision >= 0 && chip->type == SMBB) +#else if (chip->revision > 0 && chip->type == SMBB) +#endif chip->flags |= CHG_FLAGS_VCP_WA; if (chip->type == SMBB) chip->flags |= BOOST_FLASH_WA; @@ -3876,6 +4904,8 @@ qpnp_chg_request_irqs(struct qpnp_chg_chip *chip) enable_irq_wake(chip->batt_pres.irq); +/* */ +#ifndef CONFIG_LGE_PM chip->batt_temp_ok.irq = spmi_get_irq_byname(spmi, spmi_resource, "bat-temp-ok"); if (chip->batt_temp_ok.irq < 0) { @@ -3891,10 +4921,9 @@ qpnp_chg_request_irqs(struct qpnp_chg_chip *chip) chip->batt_temp_ok.irq, rc); return rc; } - qpnp_chg_bat_if_batt_temp_irq_handler(0, chip); enable_irq_wake(chip->batt_temp_ok.irq); - +#endif break; case SMBB_BUCK_SUBTYPE: case SMBBP_BUCK_SUBTYPE: @@ -4005,6 +5034,74 @@ qpnp_chg_request_irqs(struct qpnp_chg_chip *chip) return rc; } +#ifdef CONFIG_LGE_USING_CHG_LED +static void qpnp_chg_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct qpnp_chg_chip *chip = container_of(led_cdev, struct qpnp_chg_chip, cdev); + int rc; + int duty_us; + + chip->cdev.brightness = value; + if(chip->cdev.brightness > 255) + chip->cdev.brightness = 255; + + if(chip->cdev.brightness < 0) + chip->cdev.brightness = 0; + + duty_us = ((chip->pwm_cfg->pwm_period_us * chip->cdev.brightness)/255); + + pwm_config(chip->pwm_cfg->pwm_dev, duty_us, chip->pwm_cfg->pwm_period_us); + pwm_enable(chip->pwm_cfg->pwm_dev); + + if(value) { + rc = qpnp_chg_masked_write(chip, + chip->chgr_base + 0x4D, + 0x3, 0x3, 1); + } + else { + rc = qpnp_chg_masked_write(chip, + chip->chgr_base + 0x4D, + 0x3, 0x0, 1); + } +} +static enum led_brightness qpnp_chg_led_get(struct led_classdev *led_cdev) +{ + struct qpnp_chg_chip *chip = container_of(led_cdev, struct qpnp_chg_chip, cdev); + + return chip->cdev.brightness; +} +#endif + +#ifdef CONFIG_LGE_PM_THERMAL +static int qpnp_thermal_mitigation; +static int +qpnp_set_thermal_chg_current(const char *val, struct kernel_param *kp) +{ + int ret; + + ret = param_set_int(val, kp); + if (ret) { + pr_err("error setting value %d\n", ret); + return ret; + } + + if (!qpnp_chg) { + pr_err("called before init\n"); + return ret; + } + if (qpnp_thermal_mitigation <= 0) + qpnp_chg->chg_current_te = qpnp_chg->max_bat_chg_current; + else + qpnp_chg->chg_current_te = qpnp_thermal_mitigation; + + pr_err("thermal-engine set charging current to %d(mA)\n", qpnp_chg->chg_current_te); + + return 0; +} +module_param_call(qpnp_thermal_mitigation, qpnp_set_thermal_chg_current, + param_get_uint, &qpnp_thermal_mitigation, 0644); +#endif static int qpnp_chg_load_battery_data(struct qpnp_chg_chip *chip) @@ -4344,6 +5441,17 @@ qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype, return rc; } +/* */ +#ifdef CONFIG_LGE_PM +static unsigned int cable_smem_size; +unsigned int lge_chg_cable_type(void) +{ + return cable_type; +} +EXPORT_SYMBOL(lge_chg_cable_type); +#endif +/* */ + #define OF_PROP_READ(chip, prop, qpnp_dt_property, retval, optional) \ do { \ if (retval) \ @@ -4385,7 +5493,6 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) OF_PROP_READ(chip, cold_batt_p, "batt-cold-percentage", rc, 1); OF_PROP_READ(chip, soc_resume_limit, "resume-soc", rc, 1); OF_PROP_READ(chip, batt_weak_voltage_mv, "vbatweak-mv", rc, 1); - OF_PROP_READ(chip, vbatdet_max_err_mv, "vbatdet-maxerr-mv", rc, 1); if (rc) return rc; @@ -4404,8 +5511,11 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) } } - if (!chip->vbatdet_max_err_mv) - chip->vbatdet_max_err_mv = VBATDET_MAX_ERR_MV; +/* */ +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP + chip->ext_ovp_gpio = of_get_named_gpio_flags(chip->spmi->dev.of_node, "lge,ext_ovp_gpio" ,0 ,NULL ); +#endif +/* */ /* Look up JEITA compliance parameters if cool and warm temp provided */ if (chip->cool_bat_decidegc || chip->warm_bat_decidegc) { @@ -4466,9 +5576,16 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) of_property_read_bool(chip->spmi->dev.of_node, "qcom,ibat-calibration-enabled"); +#ifndef CONFIG_LGE_PM of_get_property(chip->spmi->dev.of_node, "qcom,thermal-mitigation", &(chip->thermal_levels)); +#endif + +#ifdef CONFIG_LGE_PM_THERMAL + chip->chg_current_te = chip->max_bat_chg_current; +#endif +#ifndef CONFIG_LGE_PM if (chip->thermal_levels > sizeof(int)) { chip->thermal_mitigation = devm_kzalloc(chip->dev, chip->thermal_levels, @@ -4488,9 +5605,241 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip) return rc; } } +#endif return rc; } +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO +static void qpnp_monitor_batt_temp(struct work_struct *work) +{ + struct qpnp_chg_chip *chip = + container_of(work, struct qpnp_chg_chip, battemp_work.work); + struct charging_info req; + struct charging_rsp res; + union power_supply_propval ret = {0,}; + + if(lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO && !is_enter_first) + { + pr_info("===== [qpnp_monitor_batt_temp] PM STAY AWAKE !!! ======\n"); + pm_stay_awake(chip->dev); + } + + chip->batt_psy.get_property(&(chip->batt_psy), + POWER_SUPPLY_PROP_TEMP, &ret); + req.batt_temp = ret.intval; + + chip->batt_psy.get_property(&(chip->batt_psy), + POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret); + req.batt_volt = ret.intval; + + req.is_charger = qpnp_chg_is_usb_chg_plugged_in(chip); + +#ifdef CONFIG_LGE_PM_THERMAL + req.chg_current_ma = chip->max_bat_chg_current; + req.chg_current_te = chip->chg_current_te; +#endif + // Added this code to update state when charger is reconnected during wait delay. + if ( chip->is_charger_changed_from_irq == true ){ + req.is_charger_changed = true; + chip->is_charger_changed_from_irq = false; + } + else + req.is_charger_changed = false; + + lge_monitor_batt_temp(req, &res); + + + if (((res.change_lvl != STS_CHE_NONE) && req.is_charger) || + (res.force_update == true)) { + if (res.change_lvl == STS_CHE_NORMAL_TO_DECCUR || + (res.force_update == true && res.state == CHG_BATT_DECCUR_STATE && +#ifdef CONFIG_LGE_PM_VZW_CHARGING_TEMP_SCENARIO + res.dc_current != DC_CURRENT_DEF && res.change_lvl != STS_CHE_STPCHG_TO_DECCUR)) { +#else + res.dc_current != DC_CURRENT_DEF)) { +#endif + qpnp_chg_ibatmax_set(chip, res.dc_current); + } else if (res.change_lvl == STS_CHE_NORMAL_TO_STPCHG || + (res.force_update == true && + res.state == CHG_BATT_STPCHG_STATE)) { + wake_lock(&chip->lcs_wake_lock); +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + schedule_work(&chip->cancel_input_current_check_work); +#endif + cancel_delayed_work_sync(&chip->eoc_work); + pm_relax(chip->dev); //Bug fix. When stopping charging at high temperature, device does not enter suspend state after disconnecting charger. + qpnp_chg_charge_en(chip, !res.disable_chg); + } else if (res.change_lvl == STS_CHE_DECCUR_TO_NORAML) { +#ifdef CONFIG_LGE_PM_THERMAL + qpnp_chg_ibatmax_set(chip,res.dc_current); +#else + qpnp_chg_ibatmax_set(chip,chip->max_bat_chg_current); +#endif + } else if (res.change_lvl == STS_CHE_DECCUR_TO_STPCHG) { + wake_lock(&chip->lcs_wake_lock); + qpnp_chg_ibatmax_set(chip,chip->max_bat_chg_current); + cancel_delayed_work_sync(&chip->eoc_work); + pm_relax(chip->dev); // Big fix. When stopping charging at high temperature, device does not enter suspend state after disconnecting charger. + qpnp_chg_charge_en(chip, !res.disable_chg); + } else if (res.change_lvl == STS_CHE_STPCHG_TO_NORMAL) { +#ifdef CONFIG_LGE_PM_THERMAL + qpnp_chg_ibatmax_set(chip,res.dc_current); +#endif + schedule_delayed_work(&chip->eoc_work, + msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); + qpnp_chg_charge_en(chip, !res.disable_chg); + wake_unlock(&chip->lcs_wake_lock); + } +#ifdef CONFIG_LGE_PM_VZW_CHARGING_TEMP_SCENARIO + else if (res.change_lvl == STS_CHE_STPCHG_TO_DECCUR) { +#ifdef CONFIG_LGE_PM_THERMAL + qpnp_chg_ibatmax_set(chip,res.dc_current); +#endif + schedule_delayed_work(&chip->eoc_work, + msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); + qpnp_chg_charge_en(chip, !res.disable_chg); + wake_unlock(&chip->lcs_wake_lock); + } +#endif +#ifdef CONFIG_LGE_PM_THERMAL + else if (res.force_update == true && res.state == CHG_BATT_NORMAL_STATE && + res.dc_current != DC_CURRENT_DEF) { + qpnp_chg_ibatmax_set(chip,res.dc_current); + schedule_delayed_work(&chip->eoc_work, + msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); + } +#endif + + chip->pseudo_ui_chg = res.pseudo_chg_ui; + chip->not_chg = res.state; + chip->btm_state = res.btm_state; + } + + power_supply_changed(&chip->batt_psy); + + if(req.is_charger){ + if(res.state == CHG_BATT_NORMAL_STATE){ + schedule_delayed_work(&chip->battemp_work, + MONITOR_BATTEMP_POLLING_PERIOD); + }else if(res.state == CHG_BATT_DECCUR_STATE || res.state == CHG_BATT_WARNIG_STATE){ + schedule_delayed_work(&chip->battemp_work, + MONITOR_BATTEMP_POLLING_PERIOD/3); + }else if(res.state == CHG_BATT_STPCHG_STATE){ + schedule_delayed_work(&chip->battemp_work, + MONITOR_BATTEMP_POLLING_PERIOD/6); + } + } + else { +/* Bug fix. When resuming device at high temperature, device does not enter suspend state. */ + if (wake_lock_active(&chip->lcs_wake_lock)) + wake_unlock(&chip->lcs_wake_lock); + } +} +#endif + + +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO +u32 lge_get_bl_level(void); + +static bool key_filter_end = false; +static bool key_filter_start = false; +static int prev_key_val = 1; +static int prev_key_code = 0; + +#define QPNP_PWR_KEY_MONITOR_PERIOD_MS 500 +#define KEY_UP_EVENT 0 + +void +qpnp_goto_suspend_for_chg_logo(void) +{ + if(lge_get_boot_mode() == LGE_BOOT_MODE_CHARGERLOGO && !is_enter_first) + { + pr_info("===== [qpnp_goto_suspend_for_chg_logo] PM RELAX !!! ======\n"); + + key_filter_start = false; + key_filter_end = true; + + if (qpnp_chg != NULL) { + pm_relax(qpnp_chg->dev); + } + } +} +EXPORT_SYMBOL(qpnp_goto_suspend_for_chg_logo); + + +static void +qpnp_pwr_key_filter_delay_for_chg_logo(struct work_struct *work) +{ + pr_info("=== Key filter timer expired (SoC status is %d ======\n", !is_enter_first); + + key_filter_start = false; + key_filter_end = true; +} + +void +qpnp_pwr_key_action_set_for_chg_logo(struct input_dev *dev, unsigned int code, int value) +{ + if (qpnp_chg == NULL) { + return; + } + else { + pm_stay_awake(qpnp_chg->dev); + } + + if (value == 0 && code != prev_key_code){ + // We'll receive only one key in onetime. + pr_info("=== Other key up event is detected. ignore it~\n"); + return; + } + + // ignore the key down event if key down event is happened within 300ms after key down event + // 1. KEY DOWN: value is 1 + // 2. KEY UP: value is 0 + if(prev_key_val == KEY_UP_EVENT && key_filter_start && !key_filter_end) + { + pr_info("=== Very fast key input. return it~!!! ======\n"); + return; + } + + if(prev_key_val == KEY_UP_EVENT && value == KEY_UP_EVENT && !key_filter_start) + { + pr_info("=== Not matched key pair. return it~!!! ======\n"); + return; + } + + + prev_key_val = value; + + if(key_filter_start && !key_filter_end) + { + key_filter_start = false; + key_filter_end = true; + + cancel_delayed_work_sync(&qpnp_chg->pwr_key_monitor_for_chg_logo); + return; + } + + pr_info("=== Key posting code is %d value is %d ======\n", code, value); + prev_key_code = code; + input_report_key(dev, code, value); + input_sync(dev); + + if(value == KEY_UP_EVENT) + { + key_filter_start = true; + key_filter_end = false; + schedule_delayed_work(&qpnp_chg->pwr_key_monitor_for_chg_logo, msecs_to_jiffies(QPNP_PWR_KEY_MONITOR_PERIOD_MS)); + } + else + { + key_filter_start = false; + + power_supply_changed(&qpnp_chg->batt_psy); + } +} +EXPORT_SYMBOL(qpnp_pwr_key_action_set_for_chg_logo); +#endif + static int __devinit qpnp_charger_probe(struct spmi_device *spmi) @@ -4500,6 +5849,23 @@ qpnp_charger_probe(struct spmi_device *spmi) struct resource *resource; struct spmi_resource *spmi_resource; int rc = 0; +#ifdef CONFIG_LGE_PM_WORKAROUND_PORT_OPEN_FAIL_IN_FACTORY_TEST + enum lge_boot_mode_type boot_mode = 0; +#endif + +/* */ +#ifdef CONFIG_LGE_PM + unsigned int *p_cable_type = (unsigned int *) + (smem_get_entry(SMEM_ID_VENDOR1, &cable_smem_size)); + + if (p_cable_type) + cable_type = *p_cable_type; + else + cable_type = 0; + + pr_info("[LGE] cable_type is = %d\n", cable_type); +#endif +/* */ chip = devm_kzalloc(&spmi->dev, sizeof(struct qpnp_chg_chip), GFP_KERNEL); @@ -4519,6 +5885,11 @@ qpnp_charger_probe(struct spmi_device *spmi) rc = -EPROBE_DEFER; goto fail_chg_enable; } +#ifdef CONFIG_LGE_PM_USB_ID + rc = lge_pm_get_cable_data_from_dt(spmi->dev.of_node); + if (rc) + goto fail_chg_enable; +#endif mutex_init(&chip->jeita_configure_lock); spin_lock_init(&chip->usbin_health_monitor_lock); @@ -4531,7 +5902,10 @@ qpnp_charger_probe(struct spmi_device *spmi) qpnp_chg_batfet_lcl_work); INIT_WORK(&chip->insertion_ocv_work, qpnp_chg_insertion_ocv_work); - +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + INIT_WORK(&chip->batt_temp_cancel_work, + lge_pm_batt_temp_cancel_work); +#endif /* Get all device tree properties */ rc = qpnp_charger_read_dt_props(chip); if (rc) @@ -4574,6 +5948,9 @@ qpnp_charger_probe(struct spmi_device *spmi) pr_err("vadc property missing\n"); goto fail_chg_enable; } +#ifdef CONFIG_LGE_PM_USB_ID + lge_pm_set_usb_id_handle(chip->vadc_dev); +#endif if (subtype == SMBB_BAT_IF_SUBTYPE) { chip->iadc_dev = qpnp_get_iadc(chip->dev, @@ -4620,6 +5997,21 @@ qpnp_charger_probe(struct spmi_device *spmi) case SMBBP_CHGR_SUBTYPE: case SMBCL_CHGR_SUBTYPE: chip->chgr_base = resource->start; +#ifdef CONFIG_LGE_USING_CHG_LED + chip->cdev.name = "red"; + chip->cdev.max_brightness = 0; + chip->cdev.default_trigger = "none"; + chip->cdev.brightness_set = qpnp_chg_led_set; + chip->cdev.brightness_get = qpnp_chg_led_get; + rc = led_classdev_register(&spmi->dev, &chip->cdev); + chip->pwm_cfg = devm_kzalloc(chip->dev, + sizeof(struct pwm_config_data), + GFP_KERNEL); + chip->pwm_cfg->pwm_channel = 2; + chip->pwm_cfg->pwm_dev = pwm_request(chip->pwm_cfg->pwm_channel, "chg_led"); + chip->pwm_cfg->pwm_period_us = 20000; +#endif + rc = qpnp_chg_hwinit(chip, subtype, spmi_resource); if (rc) { pr_err("Failed to init subtype 0x%x rc=%d\n", @@ -4722,8 +6114,18 @@ qpnp_charger_probe(struct spmi_device *spmi) dev_set_drvdata(&spmi->dev, chip); device_init_wakeup(&spmi->dev, 1); - chip->insertion_ocv_uv = -EINVAL; - chip->batt_present = qpnp_chg_is_batt_present(chip); + chip->insertion_ocv_uv = -EINVAL; + chip->batt_present = qpnp_chg_is_batt_present(chip); + +/* */ +#ifdef CONFIG_LGE_PM + if(is_factory_cable()){ + pr_info("Factory cable is detected, set IUSB to MAX.\n"); + qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MAX_MA); + } +#endif +/* */ + if (chip->bat_if_base) { chip->batt_psy.name = "battery"; chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY; @@ -4755,9 +6157,53 @@ qpnp_charger_probe(struct spmi_device *spmi) INIT_DELAYED_WORK(&chip->arb_stop_work, qpnp_arb_stop_work); INIT_DELAYED_WORK(&chip->usbin_health_check, qpnp_usbin_health_check_work); +#ifdef CONFIG_LGE_PM_WORKAROUND_USB_VALID_BY_REVERSE_BOOST + INIT_DELAYED_WORK(&chip->property_work, qpnp_property_work); +#endif +#ifdef CONFIG_LGE_PM_PWR_KEY_FOR_CHG_LOGO + INIT_DELAYED_WORK(&chip->pwr_key_monitor_for_chg_logo, qpnp_pwr_key_filter_delay_for_chg_logo); +#endif + +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START INIT_WORK(&chip->soc_check_work, qpnp_chg_soc_check_work); - INIT_DELAYED_WORK(&chip->aicl_check_work, qpnp_aicl_check_work); - +#endif + INIT_DELAYED_WORK(&chip->aicl_check_work, qpnp_aicl_check_work); +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + INIT_WORK(&chip->resume_check_work, qpnp_chg_resume_check_work); +#endif +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + chip->is_charger_changed_from_irq = false; + wake_lock_init(&chip->lcs_wake_lock, + WAKE_LOCK_SUSPEND, "LGE charging scenario"); + INIT_DELAYED_WORK(&chip->battemp_work, qpnp_monitor_batt_temp); +#endif + +#ifdef CONFIG_LGE_PM + wake_lock_init(&chip->uevent_wake_lock, WAKE_LOCK_SUSPEND, "qpnp_chg_uevent"); +#endif + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + INIT_DELAYED_WORK(&chip->input_current_check_work, vzw_fast_chg_input_current_check_work); + INIT_WORK(&chip->cancel_input_current_check_work, vzw_fast_chg_cancel_input_current_check_work); +#endif + + /* */ + /* */ +#ifdef CONFIG_LGE_PM + chip->dc_psy.name = "ac"; + chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS; + chip->dc_psy.supplied_to = pm_power_supplied_to; + chip->dc_psy.num_supplicants = ARRAY_SIZE(pm_power_supplied_to); + chip->dc_psy.properties = pm_power_props_mains; + chip->dc_psy.num_properties = ARRAY_SIZE(pm_power_props_mains); + chip->dc_psy.set_property = qpnp_power_set_property_mains; + chip->dc_psy.get_property = qpnp_power_get_property_mains; + rc = power_supply_register(chip->dev, &chip->dc_psy); + if (rc < 0) { + pr_err("power_supply_register dc failed rc=%d\n", rc); + goto unregister_batt; + } +#else // Qualcomm Original if (chip->dc_chgpth_base) { chip->dc_psy.name = "qpnp-dc"; chip->dc_psy.type = POWER_SUPPLY_TYPE_MAINS; @@ -4776,6 +6222,8 @@ qpnp_charger_probe(struct spmi_device *spmi) goto unregister_batt; } } +#endif + /* */ /* Turn on appropriate workaround flags */ rc = qpnp_chg_setup_flags(chip); @@ -4820,8 +6268,16 @@ qpnp_charger_probe(struct spmi_device *spmi) qpnp_chg_charge_en(chip, !chip->charging_disabled); qpnp_chg_force_run_on_batt(chip, chip->charging_disabled); - qpnp_chg_set_appropriate_vddmax(chip); - + qpnp_chg_set_appropriate_vddmax(chip); +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + schedule_delayed_work(&chip->battemp_work, + MONITOR_BATTEMP_POLLING_PERIOD / 12); +#endif + +#ifdef CONFIG_LGE_PM + chip->chg_fail_irq_happen = false; + qpnp_chg = chip; +#endif rc = qpnp_chg_request_irqs(chip); if (rc) { pr_err("failed to request interrupts %d\n", rc); @@ -4834,10 +6290,36 @@ qpnp_charger_probe(struct spmi_device *spmi) power_supply_set_present(chip->usb_psy, qpnp_chg_is_usb_chg_plugged_in(chip)); +/* */ +#ifndef CONFIG_LGE_PM + /* + */ /* Set USB psy online to avoid userspace from shutting down if battery * capacity is at zero and no chargers online. */ if (qpnp_chg_is_usb_chg_plugged_in(chip)) power_supply_set_online(chip->usb_psy, 1); +#endif +/* */ + +#ifdef CONFIG_LGE_PM_WORKAROUND_PORT_OPEN_FAIL_IN_FACTORY_TEST + boot_mode = lge_get_boot_mode(); + printk("[qpnp-charger OVD] start qpnp_chg_masked_write boot_mode = %d\n", boot_mode); + if( (boot_mode == LGE_BOOT_MODE_QEM_56K) || + (boot_mode == LGE_BOOT_MODE_QEM_130K) || + (boot_mode == LGE_BOOT_MODE_QEM_910K) ){ + + rc = qpnp_chg_masked_write(chip, + chip->usb_chgpth_base + USB_OVP_CTL, + 0x30, 0x30, 1); + printk("[qpnp-charger OVD] OVD = 7V, address = 0x1342, value = 0x30, rc = %d!!\n", rc); + }else{ + rc = qpnp_chg_masked_write(chip, + chip->usb_chgpth_base + USB_OVP_CTL, + 0x30, 0x20, 1); + printk("[qpnp-charger OVD] OVD = 6.5V, address = 0x1342, value = 0x20, rc = %d!!\n", rc); + } +#endif + schedule_delayed_work(&chip->aicl_check_work, msecs_to_jiffies(EOC_CHECK_PERIOD_MS)); @@ -4848,6 +6330,7 @@ qpnp_charger_probe(struct spmi_device *spmi) qpnp_chg_is_dc_chg_plugged_in(chip), get_prop_batt_present(chip), get_prop_batt_health(chip)); + return 0; unregister_dc_psy: @@ -4874,7 +6357,13 @@ qpnp_charger_remove(struct spmi_device *spmi) cancel_delayed_work_sync(&chip->aicl_check_work); power_supply_unregister(&chip->dc_psy); +#ifndef CONFIG_LGE_PM_4_25V_CHARGING_START cancel_work_sync(&chip->soc_check_work); +#endif + +#ifdef CONFIG_LGE_PM_4_25V_CHARGING_START + cancel_work_sync(&chip->resume_check_work); +#endif cancel_delayed_work_sync(&chip->usbin_health_check); cancel_delayed_work_sync(&chip->arb_stop_work); cancel_delayed_work_sync(&chip->eoc_work); @@ -4908,6 +6397,9 @@ static int qpnp_chg_resume(struct device *dev) if (rc) pr_debug("failed to force on VREF_BAT_THM rc=%d\n", rc); } +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + schedule_delayed_work(&chip->battemp_work, HZ*10); +#endif return rc; } @@ -4925,10 +6417,46 @@ static int qpnp_chg_suspend(struct device *dev) if (rc) pr_debug("failed to set FSM VREF_BAT_THM rc=%d\n", rc); } + +#ifdef CONFIG_LGE_PM_CHARGING_TEMP_SCENARIO + cancel_delayed_work_sync(&chip->battemp_work); +#endif return rc; } +#ifdef CONFIG_LGE_PM_CHARGING_EXTERNAL_OVP +bool lge_check_fast_chg_irq(void) +{ + int rc; + u8 chgr_sts; + + rc = qpnp_chg_read(qpnp_chg, &chgr_sts, INT_RT_STS(qpnp_chg->chgr_base), 1); + if (rc) { + pr_err("failed to read interrupt sts %d\n", rc); + return false; + } + + if(chgr_sts & FAST_CHG_ON_IRQ) + return true; + + return false; +} +EXPORT_SYMBOL(lge_check_fast_chg_irq); + +void lge_set_chg_path_to_external(void) +{ + lge_pm_set_ext_ovp(qpnp_chg->ext_ovp_gpio, EXT_OVP_CTRL_HIGH); +} +EXPORT_SYMBOL(lge_set_chg_path_to_external); + +void lge_set_chg_path_to_internal(void) +{ + lge_pm_set_ext_ovp(qpnp_chg->ext_ovp_gpio, EXT_OVP_CTRL_LOW); +} +EXPORT_SYMBOL(lge_set_chg_path_to_internal); +#endif + static const struct dev_pm_ops qpnp_chg_pm_ops = { .resume = qpnp_chg_resume, .suspend = qpnp_chg_suspend, diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 8287ad7200df..19a19b538376 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,17 @@ #include #include +/* */ +#ifdef CONFIG_LGE_PM +#include +#include +#endif +/* */ + +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG +extern void set_vzw_usb_charging_state(int state); +#endif + #define MSM_USB_BASE (motg->regs) #define DRIVER_NAME "msm_otg" @@ -1074,7 +1086,15 @@ static int msm_otg_suspend(struct msm_otg *motg) if (motg->caps & ALLOW_PHY_POWER_COLLAPSE && !host_bus_suspend && !dcp) { +#ifdef CONFIG_USB_G_LGE_ANDROID +#if (defined (CONFIG_MACH_MSM8X10_W3DS_TIM_BR) || defined (CONFIG_MACH_MSM8X10_W3DS_GLOBAL_COM) || defined (CONFIG_MACH_MSM8X10_W3C_VZW)) + if(lge_get_board_revno() > HW_REV_A){ + msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF); + } +#else msm_hsusb_ldo_enable(motg, USB_PHY_REG_OFF); +#endif // (defined (CONFIG_MACH_MSM8X10_W3DS_TIM_BR) || defined (CONFIG_MACH_MSM8X10_W3DS_GLOBAL_COM) || defined (CONFIG_MACH_MSM8X10_W3C_VZW)) +#endif // motg->lpm_flags |= PHY_PWR_COLLAPSED; } else if (motg->caps & ALLOW_PHY_REGULATORS_LPM && !host_bus_suspend && !device_bus_suspend && !dcp) { @@ -1173,7 +1193,15 @@ static int msm_otg_resume(struct msm_otg *motg) } if (motg->lpm_flags & PHY_PWR_COLLAPSED) { +#ifdef CONFIG_USB_G_LGE_ANDROID +#if (defined (CONFIG_MACH_MSM8X10_W3DS_TIM_BR) || defined (CONFIG_MACH_MSM8X10_W3DS_GLOBAL_COM) || defined (CONFIG_MACH_MSM8X10_W3C_VZW)) + if(lge_get_board_revno() > HW_REV_A){ + msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON); + } +#else msm_hsusb_ldo_enable(motg, USB_PHY_REG_ON); +#endif // (defined (CONFIG_MACH_MSM8X10_W3DS_TIM_BR) || defined (CONFIG_MACH_MSM8X10_W3DS_GLOBAL_COM) || defined (CONFIG_MACH_MSM8X10_W3C_VZW)) +#endif // motg->lpm_flags &= ~PHY_PWR_COLLAPSED; } else if (motg->lpm_flags & PHY_REGULATORS_LPM) { msm_hsusb_ldo_enable(motg, USB_PHY_REG_LPM_OFF); @@ -1340,6 +1368,11 @@ static int msm_otg_notify_chg_type(struct msm_otg *motg) return 0; } +#ifdef CONFIG_LGE_PM +static unsigned prev_mA = 0; +#endif + + static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA) { if (!psy) { @@ -1347,28 +1380,183 @@ static int msm_otg_notify_power_supply(struct msm_otg *motg, unsigned mA) goto psy_error; } +/* */ +#ifdef CONFIG_LGE_PM + if (motg->chg_type == USB_DCP_CHARGER || motg->chg_type == USB_PROPRIETARY_CHARGER || + motg->chg_type == USB_FLOATED_CHARGER){ + pr_info("\n[LGE]msm_otg_notify_power_supply: " + "power_supply_get_by_name(ac)\n"); + psy = power_supply_get_by_name("ac"); + } else { + pr_info("\n[LGE] msm_otg_notify_power_supply: " + "power_supply_get_by_name(usb)\n"); + psy = power_supply_get_by_name("usb"); + } + if (!psy) { + goto psy_error; + } + + pr_debug("[LGE] motg->cur_power: %d mA: %d\n", motg->cur_power, mA); +#else + pr_debug("\n[LGE] msm_otg_notify_power_supply: " + "power_supply_get_by_name(usb)\n"); + psy = power_supply_get_by_name("usb"); + + if (!psy) { + goto psy_error; + } + + pr_debug("[LGE] motg->cur_power: %d mA: %d\n", motg->cur_power, mA); +#endif +/* */ + if (motg->cur_power == 0 && mA > 2) { +#ifdef CONFIG_LGE_PM + prev_mA = mA; +#endif /* Enable charging */ if (power_supply_set_online(psy, true)) goto psy_error; if (power_supply_set_current_limit(psy, 1000*mA)) goto psy_error; - } else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) { +#ifdef CONFIG_LGE_PM + power_supply_changed(psy); + + if(!strncmp(psy->name,"ac", 2)) { + psy = power_supply_get_by_name("usb"); + if (!psy) + goto psy_error; + + // if ac_online set -> copy usb online set + if (power_supply_set_online(psy, true)) + goto psy_error; + + if(power_supply_set_current_limit(psy, 1000*mA)) + goto psy_error; + power_supply_changed(psy); + + psy = power_supply_get_by_name("ac"); + if (!psy) + goto psy_error; + } +#endif + } +#ifdef CONFIG_LGE_PM + // Disconnect Mode + else if (motg->cur_power > 0 && (mA == 0)) { +#ifdef CONFIG_LGE_PM + prev_mA = mA; +#endif + /* Disable charging */ if (power_supply_set_online(psy, false)) goto psy_error; /* Set max current limit */ if (power_supply_set_current_limit(psy, 0)) goto psy_error; - } else { + +#ifdef CONFIG_LGE_PM + power_supply_changed(psy); + if(!strncmp(psy->name,"ac", 2)) { + psy = power_supply_get_by_name("usb"); + if (!psy) + goto psy_error; + + if (power_supply_set_online(psy, false)) + goto psy_error; + + if(power_supply_set_current_limit(psy, 0)) + goto psy_error; + // copy end + power_supply_changed(psy); + + psy = power_supply_get_by_name("ac"); + if (!psy) + goto psy_error; + } +#endif + + /* */ + /* Below line comes from 'msm_otg_sm_work' because of AC(TA) removal detection*/ + if(mA == 0) + motg->chg_type = USB_INVALID_CHARGER; + /* */ + } + // Suspend Mode + else if (motg->cur_power > 0 && (mA == 2)) { + /* Disable charging */ + if (power_supply_set_online(psy, true)) + goto psy_error; + /* Set max current limit */ + if (power_supply_set_current_limit(psy, 1000*prev_mA)) + goto psy_error; + + power_supply_changed(psy); + + // if ac_online set -> copy usb online set + if(!strncmp(psy->name,"ac", 2)) { + psy = power_supply_get_by_name("usb"); + if (!psy) + goto psy_error; + + if (power_supply_set_online(psy, true)) + goto psy_error; + + if(power_supply_set_current_limit(psy, 1000*prev_mA)) + goto psy_error; + // Copy end + + power_supply_changed(psy); + + psy = power_supply_get_by_name("ac"); + if (!psy) + goto psy_error; + } + } +#else + // QCT Original Code + else if (motg->cur_power > 0 && (mA == 0 || mA == 2)) { + /* Disable charging */ + //if (power_supply_set_online(psy, false)) + if (power_supply_set_online(psy, true)) + goto psy_error; + /* Set max current limit */ + //if (power_supply_set_current_limit(psy, 0)) + if (power_supply_set_current_limit(psy, 500000)) + goto psy_error; + } +#endif + else { if (power_supply_set_online(psy, true)) goto psy_error; /* Current has changed (100/2 --> 500) */ if (power_supply_set_current_limit(psy, 1000*mA)) goto psy_error; +#ifdef CONFIG_LGE_PM + power_supply_changed(psy); + if(!strncmp(psy->name,"ac", 2)) { + psy = power_supply_get_by_name("usb"); + if (!psy) + goto psy_error; + + // if ac_online set -> copy usb online set + if (power_supply_set_online(psy, true)) + goto psy_error; + + if(power_supply_set_current_limit(psy, 1000*mA)) + goto psy_error; + + power_supply_changed(psy); + psy = power_supply_get_by_name("ac"); + if (!psy) + goto psy_error; + } +#endif } +#ifndef CONFIG_LGE_PM power_supply_changed(psy); +#endif return 0; psy_error: @@ -1380,6 +1568,10 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) { struct usb_gadget *g = motg->phy.otg->gadget; +#if defined (CONFIG_LGE_PM) && defined (CONFIG_MACH_MSM8X10_W3C_VZW) + acc_cable_type cable; +#endif + if (g && g->is_a_peripheral) return; @@ -1395,6 +1587,27 @@ static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) "Failed notifying %d charger type to PMIC\n", motg->chg_type); +/* */ +#if defined (CONFIG_LGE_PM) && !defined (CONFIG_MACH_MSM8X10_W3C_VZW) + if (mA > 2 && lge_pm_get_cable_type() != NO_INIT_CABLE) { + if (motg->chg_type == USB_SDP_CHARGER) + mA = lge_pm_get_usb_current(); + else if (motg->chg_type == USB_DCP_CHARGER || motg->chg_type == USB_PROPRIETARY_CHARGER || + motg->chg_type == USB_FLOATED_CHARGER) + mA = lge_pm_get_ta_current(); + } +#elif defined (CONFIG_LGE_PM) && defined (CONFIG_MACH_MSM8X10_W3C_VZW) + cable = lge_pm_get_cable_type(); + + if (mA > 2 && cable != NO_INIT_CABLE) { + if ( cable == CABLE_56K || cable == CABLE_130K || cable == CABLE_910K ) { + mA = lge_pm_get_usb_current(); + dev_info(motg->phy.dev, "factory cable detected set current to %u\n", mA); + } + } +#endif +/* */ + if (motg->cur_power == mA) return; @@ -2296,6 +2509,57 @@ static const char *chg_to_string(enum usb_chg_type chg_type) } } +#ifdef CONFIG_LGE_PM_USB_ID +void lge_pm_set_usb_id_handle(struct qpnp_vadc_chip *usb_id_handle){ + the_msm_otg->vadc_dev = usb_id_handle; +} +int32_t lge_pm_get_cable_usb_id_adc(struct msm_otg *motg) +{ + int32_t rc; + struct qpnp_vadc_result result; + int i; + + for ( i = 0 ; i < MAX_LGE_CABLE_RETRY_COUNT ; i ++ ){ +#ifdef CONFIG_ARCH_MSM8610 + rc = qpnp_vadc_read(motg->vadc_dev,P_MUX3_1_1, &result); +#else +#if defined(CONFIG_MACH_MSM8226_W7DS_OPEN_CIS) \ + || defined(CONFIG_MACH_MSM8226_W7_OPEN_CIS) \ + || defined(CONFIG_MACH_MSM8226_W7_OPEN_EU) \ + || defined(CONFIG_MACH_MSM8226_W7_GLOBAL_COM) \ + || defined(CONFIG_MACH_MSM8226_W7_GLOBAL_SCA) \ + || defined(CONFIG_MACH_MSM8226_W7DS_GLOBAL_SCA)\ + || defined(CONFIG_MACH_MSM8226_W7DS_GLOBAL_COM) + if(lge_get_board_revno() == HW_REV_0) + rc = qpnp_vadc_read(motg->vadc_dev,P_MUX8_1_1, &result); + else + rc = qpnp_vadc_read(motg->vadc_dev,LR_MUX10_USB_ID_LV, &result); +#else + rc = qpnp_vadc_read(motg->vadc_dev,LR_MUX10_USB_ID_LV, &result); +#endif +#endif + if (rc < 0) { + pr_err("%s: adc read error - %d remaing retry count is - %d\n",__func__, rc, MAX_LGE_CABLE_RETRY_COUNT-i-1); + mdelay(10); + continue; + } + } + + if ( rc < 0 ){ + return rc; + } + else{ + pr_info("%s: adc read value is %d\n",__func__, (int32_t)result.physical); + return (int32_t)result.physical; + } +} +#endif + +#ifdef CONFIG_MACH_MSM8926_X5_VZW /* Detecting Cradle for VZW */ +extern void carkit_set_deskdock(int state); +extern int carkit_get_deskdock(void); +#endif + #define MSM_CHG_DCD_TIMEOUT (750 * HZ/1000) /* 750 msec */ #define MSM_CHG_DCD_POLL_TIME (50 * HZ/1000) /* 50 msec */ #define MSM_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */ @@ -2308,6 +2572,9 @@ static void msm_chg_detect_work(struct work_struct *w) static bool dcd; u32 line_state, dm_vlgc; unsigned long delay; +#ifdef CONFIG_LGE_PM + int32_t usb_adc_val; +#endif dev_dbg(phy->dev, "chg detection work\n"); @@ -2400,6 +2667,27 @@ static void msm_chg_detect_work(struct work_struct *w) motg->chg_state = USB_CHG_STATE_DETECTED; delay = 0; } +#ifdef CONFIG_LGE_PM_USB_ID + if ( !motg->vadc_dev ) {// when adc is not initialized + pr_err("%s : ADC is not initiliazed. Set the minimum current.\n",__func__); + lge_pm_set_usb_cable_to_minimum(); // When Initial has a problem + } + else { + usb_adc_val = lge_pm_get_cable_usb_id_adc(motg); + if ( usb_adc_val < 0 ) { + // Error : Device can not get the USB ID when adc block in in hang. + pr_err("%s : ADC is not ready of in hang. Set the minimum current.\n",__func__); + lge_pm_set_usb_cable_to_minimum(); + } + else + lge_pm_read_cable_info(usb_adc_val); +#ifdef CONFIG_MACH_MSM8926_X5_VZW /* Detecting Cradle for VZW */ + if ( lge_pm_get_cable_type() == CABLE_270K ) { + carkit_set_deskdock(1); + } +#endif + } +#endif break; case USB_CHG_STATE_PRIMARY_DONE: vout = msm_chg_check_secondary_det(motg); @@ -2479,6 +2767,7 @@ static void msm_otg_init_sm(struct msm_otg *motg) else clear_bit(ID, &motg->inputs); } + /* * VBUS initial state is reported after PMIC * driver initialization. Wait for it. @@ -2544,13 +2833,26 @@ static void msm_otg_wait_for_ext_chg_done(struct msm_otg *motg) } } +#if defined (CONFIG_TOUCHSCREEN_ATMEL_S336) || defined (CONFIG_LGE_TOUCHSCREEN_SYNAPTIC) +extern void trigger_usb_state_from_otg(int usb_type); +#endif + static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); struct usb_otg *otg = motg->phy.otg; bool work = 0, srp_reqd, dcp; +#ifdef CONFIG_USB_G_LGE_ANDROID + struct msm_otg_platform_data *pdata = motg->pdata; +#endif pm_runtime_resume(otg->phy->dev); + + if(motg->pm_done) { + pm_runtime_get_sync(otg->phy->dev); + motg->pm_done = 0; + } + pr_debug("%s work\n", otg_state_string(otg->phy->state)); switch (otg->phy->state) { case OTG_STATE_UNDEFINED: @@ -2596,6 +2898,9 @@ static void msm_otg_sm_work(struct work_struct *w) case USB_CHG_STATE_DETECTED: switch (motg->chg_type) { case USB_DCP_CHARGER: +#if defined (CONFIG_TOUCHSCREEN_ATMEL_S336) || defined (CONFIG_LGE_TOUCHSCREEN_SYNAPTIC) + trigger_usb_state_from_otg(USB_DCP_CHARGER); +#endif /* Enable VDP_SRC */ ulpi_write(otg->phy, 0x2, 0x85); if (motg->ext_chg_opened) { @@ -2605,8 +2910,11 @@ static void msm_otg_sm_work(struct work_struct *w) } /* fall through */ case USB_PROPRIETARY_CHARGER: - msm_otg_notify_charger(motg, - IDEV_CHG_MAX); +#if defined (CONFIG_MACH_MSM8X10_W3C_VZW) + msm_otg_notify_charger(motg, IDEV_CHG_DCP); +#else + msm_otg_notify_charger(motg, IDEV_CHG_MAX); +#endif pm_runtime_put_sync(otg->phy->dev); break; case USB_FLOATED_CHARGER: @@ -2624,11 +2932,19 @@ static void msm_otg_sm_work(struct work_struct *w) */ break; case USB_CDP_CHARGER: +#if defined (CONFIG_MACH_MSM8x10_W3C_VZW) + msm_otg_notify_charger(motg, + IDEV_CHG_CDP); +#else msm_otg_notify_charger(motg, IDEV_CHG_MAX); +#endif msm_otg_start_peripheral(otg, 1); otg->phy->state = OTG_STATE_B_PERIPHERAL; +#if defined (CONFIG_TOUCHSCREEN_ATMEL_S336) || defined (CONFIG_LGE_TOUCHSCREEN_SYNAPTICS) + trigger_usb_state_from_otg(USB_CDP_CHARGER); +#endif break; case USB_ACA_C_CHARGER: msm_otg_notify_charger(motg, @@ -2638,6 +2954,24 @@ static void msm_otg_sm_work(struct work_struct *w) OTG_STATE_B_PERIPHERAL; break; case USB_SDP_CHARGER: +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + { + bool tmout = motg->dcd_time >= MSM_CHG_DCD_TIMEOUT; + if (tmout) { + set_vzw_usb_charging_state(0 /* IS_OPEN_TA */); + break; + } + } +#endif +#if defined (CONFIG_MACH_MSM8X10_W3C_VZW) + msm_otg_notify_charger(motg, IUNIT); +#elif defined(CONFIG_LGE_PM) + msm_otg_notify_charger(motg, + IDEV_CHG_MIN); +#endif// jaegeun.jung for Setting the Charging Current +#if defined (CONFIG_TOUCHSCREEN_ATMEL_S336) || defined (CONFIG_LGE_TOUCHSCREEN_SYNAPTIC) + trigger_usb_state_from_otg(USB_SDP_CHARGER); +#endif msm_otg_start_peripheral(otg, 1); otg->phy->state = OTG_STATE_B_PERIPHERAL; @@ -2669,7 +3003,17 @@ static void msm_otg_sm_work(struct work_struct *w) cancel_delayed_work_sync(&motg->chg_work); dcp = (motg->chg_type == USB_DCP_CHARGER); motg->chg_state = USB_CHG_STATE_UNDEFINED; + /* */ + /* Below line is moved to 'msm_otg_notify_power_supply' + * for AC(TA) removal detection + */ + #ifndef CONFIG_LGE_PM motg->chg_type = USB_INVALID_CHARGER; + #endif + /* */ +#if defined (CONFIG_TOUCHSCREEN_ATMEL_S336) || defined (CONFIG_LGE_TOUCHSCREEN_SYNAPTIC) + trigger_usb_state_from_otg(0); +#endif msm_otg_notify_charger(motg, 0); if (dcp) { msm_otg_wait_for_ext_chg_done(motg); @@ -2683,12 +3027,21 @@ static void msm_otg_sm_work(struct work_struct *w) * switch from ACA to PMIC. Check ID state * before entering into low power mode. */ - if (!msm_otg_read_pmic_id_state(motg)) { - pr_debug("process missed ID intr\n"); - clear_bit(ID, &motg->inputs); - work = 1; - break; +#ifdef CONFIG_USB_G_LGE_ANDROID + if (pdata->mode != USB_PERIPHERAL) { + if (!msm_otg_read_pmic_id_state(motg)) { + pr_debug("process missed ID intr\n"); + clear_bit(ID, &motg->inputs); + work = 1; + break; + } + } +#endif +#ifdef CONFIG_MACH_MSM8926_X5_VZW /* Detecting Cradle for VZW */ + if (carkit_get_deskdock()) { + carkit_set_deskdock(0); } +#endif pm_runtime_put_noidle(otg->phy->dev); /* * Only if autosuspend was enabled in probe, it will be @@ -2696,6 +3049,7 @@ static void msm_otg_sm_work(struct work_struct *w) */ pm_runtime_mark_last_busy(otg->phy->dev); pm_runtime_autosuspend(otg->phy->dev); + motg->pm_done = 1; } break; case OTG_STATE_B_SRP_INIT: @@ -2730,6 +3084,9 @@ static void msm_otg_sm_work(struct work_struct *w) test_bit(B_FALSE_SDP, &motg->inputs)) { pr_debug("B_FALSE_SDP\n"); msm_otg_start_peripheral(otg, 0); +#ifdef CONFIG_LGE_PM + msm_otg_notify_charger(motg,0); +#endif // usb power supply present value set as 0 motg->chg_type = USB_DCP_CHARGER; clear_bit(B_FALSE_SDP, &motg->inputs); otg->phy->state = OTG_STATE_B_IDLE; @@ -4842,6 +5199,7 @@ static int msm_otg_runtime_resume(struct device *dev) dev_dbg(dev, "OTG runtime resume\n"); pm_runtime_get_noresume(dev); + motg->pm_done = 0; return msm_otg_resume(motg); } #endif @@ -4868,6 +5226,7 @@ static int msm_otg_pm_resume(struct device *dev) struct msm_otg *motg = dev_get_drvdata(dev); dev_dbg(dev, "OTG PM resume\n"); + motg->pm_done = 0; atomic_set(&motg->pm_suspended, 0); if (motg->async_int || motg->sm_work_pending) { diff --git a/include/linux/batterydata-lib.h b/include/linux/batterydata-lib.h index ff38eb6f8d62..ae9fa0e71831 100644 --- a/include/linux/batterydata-lib.h +++ b/include/linux/batterydata-lib.h @@ -129,11 +129,48 @@ struct bms_battery_data { #if defined(CONFIG_PM8921_BMS) || \ defined(CONFIG_PM8921_BMS_MODULE) || \ defined(CONFIG_QPNP_BMS) + +#if defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2100mAh) && defined(CONFIG_MACH_MSM8X10_W5C_VZW) +#define CONFIG_LGE_PM_BATTERY_HITACI_2100mAh +#endif + +#ifdef CONFIG_LGE_PM_BATTERY_PROFILE_DATA + +#ifdef CONFIG_LGE_PM_BATTERY_CAPACITY_1540mAh +extern struct bms_battery_data LGC_BL44JR_1540_data; +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_1700mAh) +extern struct bms_battery_data LGC_BL44JH_1700_data; +extern struct bms_battery_data LG_LGC_BL44JH_1700_data; +extern struct bms_battery_data LG_TOCAD_BL44JH_1700_data; +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2440mAh) +extern struct bms_battery_data LGE_BL_59UH_2440mAh_LG_Chem_data; +extern struct bms_battery_data LGE_BL_59UH_2440mAh_TOCAD_data; +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2540mAh) +extern struct bms_battery_data LGE_BL_54SH_2540mAh_LG_Chem_data; +extern struct bms_battery_data LGE_BL_54SH_2540mAh_LG_Sanyo_data; +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_3000mAh) +extern struct bms_battery_data LGE_BL_64SH_3000mAh_LG_Chem_data; +extern struct bms_battery_data LGE_BL_64SH_3000mAh_Technohill_data; +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_2100mAh) +extern struct bms_battery_data LGE_LGC_2040mAH_data; +extern struct bms_battery_data LGE_Tocad_2040mAh_data; +#ifdef CONFIG_LGE_PM_BATTERY_HITACI_2100mAh +extern struct bms_battery_data LGE_Hitaci_2040mAh_data; +#endif +#elif defined(CONFIG_LGE_PM_BATTERY_CAPACITY_3200mAh) +extern struct bms_battery_data LGE_BL_47TH_3200mAh_LG_Chem_data; +extern struct bms_battery_data LGE_BL_47TH_3200mAh_Tocad_data; +#else +extern struct bms_battery_data LGE_BL_54SH_2540mAh_LG_Chem_data; +#endif + +#else extern struct bms_battery_data palladium_1500_data; extern struct bms_battery_data desay_5200_data; extern struct bms_battery_data oem_batt_data; extern struct bms_battery_data QRD_4v35_2000mAh_data; extern struct bms_battery_data qrd_4v2_1300mah_data; +#endif int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp); int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 0065203aa10b..171800cb7a9a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -127,8 +127,10 @@ enum power_supply_property { POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, +#ifndef CONFIG_LGE_PM POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, +#endif POWER_SUPPLY_PROP_TEMP_AMBIENT, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -136,8 +138,25 @@ enum power_supply_property { POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ POWER_SUPPLY_PROP_SCOPE, +#ifndef CONFIG_LGE_PM POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, +#endif POWER_SUPPLY_PROP_RESISTANCE, +#ifdef CONFIG_LGE_PM_FACTORY_TESTMODE + POWER_SUPPLY_PROP_CHCOMP, + POWER_SUPPLY_PROP_CHARGE, + POWER_SUPPLY_PROP_HW_REV, + POWER_SUPPLY_PROP_USB_ID, +#endif +#ifdef CONFIG_LGE_PM_FACTORY_PSEUDO_BATTERY + POWER_SUPPLY_PROP_PSEUDO_BATT, +#endif +#ifdef CONFIG_LGE_PM_BATTERY_ID_CHECKER + POWER_SUPPLY_PROP_VALID_BATT, +#endif +#ifdef CONFIG_LGE_PM_VZW_FAST_CHG + POWER_SUPPLY_PROP_VZW_CHG_STATE, +#endif /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 045a58b6962b..4be6facd4389 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -27,6 +27,10 @@ #include #include #include +#ifdef CONFIG_LGE_PM_USB_ID +#include +#endif + /* * The following are bit fields describing the usb_request.udc_priv word. * These bit fields are set by function drivers that wish to queue @@ -461,7 +465,12 @@ struct msm_otg { bool ext_chg_opened; bool ext_chg_active; struct completion ext_chg_wait; - int ui_enabled; + bool pm_done; + int ui_enabled; + +#ifdef CONFIG_LGE_PM_USB_ID + struct qpnp_vadc_chip *vadc_dev; +#endif }; struct ci13xxx_platform_data { From 867d23242ed9aab6d9c6dcafb9f90497a5072524 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 8 Jun 2015 21:49:11 -0400 Subject: [PATCH 095/104] f2fs: Initial import from kernel.org --- Documentation/ABI/testing/sysfs-fs-f2fs | 82 + Documentation/filesystems/f2fs.txt | 566 ++++++ fs/Kconfig | 1 + fs/Makefile | 1 + fs/f2fs/Kconfig | 83 + fs/f2fs/Makefile | 8 + fs/f2fs/acl.c | 403 ++++ fs/f2fs/acl.h | 59 + fs/f2fs/checkpoint.c | 1136 +++++++++++ fs/f2fs/data.c | 1859 ++++++++++++++++++ fs/f2fs/debug.c | 413 ++++ fs/f2fs/dir.c | 820 ++++++++ fs/f2fs/f2fs.h | 1819 +++++++++++++++++ fs/f2fs/file.c | 1190 +++++++++++ fs/f2fs/gc.c | 746 +++++++ fs/f2fs/gc.h | 110 ++ fs/f2fs/hash.c | 104 + fs/f2fs/inline.c | 536 +++++ fs/f2fs/inode.c | 384 ++++ fs/f2fs/namei.c | 622 ++++++ fs/f2fs/node.c | 2086 ++++++++++++++++++++ fs/f2fs/node.h | 416 ++++ fs/f2fs/recovery.c | 575 ++++++ fs/f2fs/segment.c | 2393 +++++++++++++++++++++++ fs/f2fs/segment.h | 751 +++++++ fs/f2fs/super.c | 1348 +++++++++++++ fs/f2fs/trace.c | 159 ++ fs/f2fs/trace.h | 46 + fs/f2fs/xattr.c | 617 ++++++ fs/f2fs/xattr.h | 154 ++ include/linux/f2fs_fs.h | 476 +++++ include/trace/events/f2fs.h | 1174 +++++++++++ 32 files changed, 21137 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-fs-f2fs create mode 100644 Documentation/filesystems/f2fs.txt create mode 100644 fs/f2fs/Kconfig create mode 100644 fs/f2fs/Makefile create mode 100644 fs/f2fs/acl.c create mode 100644 fs/f2fs/acl.h create mode 100644 fs/f2fs/checkpoint.c create mode 100644 fs/f2fs/data.c create mode 100644 fs/f2fs/debug.c create mode 100644 fs/f2fs/dir.c create mode 100644 fs/f2fs/f2fs.h create mode 100644 fs/f2fs/file.c create mode 100644 fs/f2fs/gc.c create mode 100644 fs/f2fs/gc.h create mode 100644 fs/f2fs/hash.c create mode 100644 fs/f2fs/inline.c create mode 100644 fs/f2fs/inode.c create mode 100644 fs/f2fs/namei.c create mode 100644 fs/f2fs/node.c create mode 100644 fs/f2fs/node.h create mode 100644 fs/f2fs/recovery.c create mode 100644 fs/f2fs/segment.c create mode 100644 fs/f2fs/segment.h create mode 100644 fs/f2fs/super.c create mode 100644 fs/f2fs/trace.c create mode 100644 fs/f2fs/trace.h create mode 100644 fs/f2fs/xattr.c create mode 100644 fs/f2fs/xattr.h create mode 100644 include/linux/f2fs_fs.h create mode 100644 include/trace/events/f2fs.h diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs new file mode 100644 index 000000000000..2c4cc42006e8 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -0,0 +1,82 @@ +What: /sys/fs/f2fs//gc_max_sleep_time +Date: July 2013 +Contact: "Namjae Jeon" +Description: + Controls the maximun sleep time for gc_thread. Time + is in milliseconds. + +What: /sys/fs/f2fs//gc_min_sleep_time +Date: July 2013 +Contact: "Namjae Jeon" +Description: + Controls the minimum sleep time for gc_thread. Time + is in milliseconds. + +What: /sys/fs/f2fs//gc_no_gc_sleep_time +Date: July 2013 +Contact: "Namjae Jeon" +Description: + Controls the default sleep time for gc_thread. Time + is in milliseconds. + +What: /sys/fs/f2fs//gc_idle +Date: July 2013 +Contact: "Namjae Jeon" +Description: + Controls the victim selection policy for garbage collection. + +What: /sys/fs/f2fs//reclaim_segments +Date: October 2013 +Contact: "Jaegeuk Kim" +Description: + Controls the issue rate of segment discard commands. + +What: /sys/fs/f2fs//ipu_policy +Date: November 2013 +Contact: "Jaegeuk Kim" +Description: + Controls the in-place-update policy. + +What: /sys/fs/f2fs//min_ipu_util +Date: November 2013 +Contact: "Jaegeuk Kim" +Description: + Controls the FS utilization condition for the in-place-update + policies. + +What: /sys/fs/f2fs//min_fsync_blocks +Date: September 2014 +Contact: "Jaegeuk Kim" +Description: + Controls the dirty page count condition for the in-place-update + policies. + +What: /sys/fs/f2fs//max_small_discards +Date: November 2013 +Contact: "Jaegeuk Kim" +Description: + Controls the issue rate of small discard commands. + +What: /sys/fs/f2fs//max_victim_search +Date: January 2014 +Contact: "Jaegeuk Kim" +Description: + Controls the number of trials to find a victim segment. + +What: /sys/fs/f2fs//dir_level +Date: March 2014 +Contact: "Jaegeuk Kim" +Description: + Controls the directory level for large directory. + +What: /sys/fs/f2fs//ram_thresh +Date: March 2014 +Contact: "Jaegeuk Kim" +Description: + Controls the memory footprint used by f2fs. + +What: /sys/fs/f2fs//trim_sections +Date: February 2015 +Contact: "Jaegeuk Kim" +Description: + Controls the trimming rate in batch mode. diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt new file mode 100644 index 000000000000..4da837c20286 --- /dev/null +++ b/Documentation/filesystems/f2fs.txt @@ -0,0 +1,566 @@ +================================================================================ +WHAT IS Flash-Friendly File System (F2FS)? +================================================================================ + +NAND flash memory-based storage devices, such as SSD, eMMC, and SD cards, have +been equipped on a variety systems ranging from mobile to server systems. Since +they are known to have different characteristics from the conventional rotating +disks, a file system, an upper layer to the storage device, should adapt to the +changes from the sketch in the design level. + +F2FS is a file system exploiting NAND flash memory-based storage devices, which +is based on Log-structured File System (LFS). The design has been focused on +addressing the fundamental issues in LFS, which are snowball effect of wandering +tree and high cleaning overhead. + +Since a NAND flash memory-based storage device shows different characteristic +according to its internal geometry or flash memory management scheme, namely FTL, +F2FS and its tools support various parameters not only for configuring on-disk +layout, but also for selecting allocation and cleaning algorithms. + +The following git tree provides the file system formatting tool (mkfs.f2fs), +a consistency checking tool (fsck.f2fs), and a debugging tool (dump.f2fs). +>> git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git + +For reporting bugs and sending patches, please use the following mailing list: +>> linux-f2fs-devel@lists.sourceforge.net + +================================================================================ +BACKGROUND AND DESIGN ISSUES +================================================================================ + +Log-structured File System (LFS) +-------------------------------- +"A log-structured file system writes all modifications to disk sequentially in +a log-like structure, thereby speeding up both file writing and crash recovery. +The log is the only structure on disk; it contains indexing information so that +files can be read back from the log efficiently. In order to maintain large free +areas on disk for fast writing, we divide the log into segments and use a +segment cleaner to compress the live information from heavily fragmented +segments." from Rosenblum, M. and Ousterhout, J. K., 1992, "The design and +implementation of a log-structured file system", ACM Trans. Computer Systems +10, 1, 26–52. + +Wandering Tree Problem +---------------------- +In LFS, when a file data is updated and written to the end of log, its direct +pointer block is updated due to the changed location. Then the indirect pointer +block is also updated due to the direct pointer block update. In this manner, +the upper index structures such as inode, inode map, and checkpoint block are +also updated recursively. This problem is called as wandering tree problem [1], +and in order to enhance the performance, it should eliminate or relax the update +propagation as much as possible. + +[1] Bityutskiy, A. 2005. JFFS3 design issues. http://www.linux-mtd.infradead.org/ + +Cleaning Overhead +----------------- +Since LFS is based on out-of-place writes, it produces so many obsolete blocks +scattered across the whole storage. In order to serve new empty log space, it +needs to reclaim these obsolete blocks seamlessly to users. This job is called +as a cleaning process. + +The process consists of three operations as follows. +1. A victim segment is selected through referencing segment usage table. +2. It loads parent index structures of all the data in the victim identified by + segment summary blocks. +3. It checks the cross-reference between the data and its parent index structure. +4. It moves valid data selectively. + +This cleaning job may cause unexpected long delays, so the most important goal +is to hide the latencies to users. And also definitely, it should reduce the +amount of valid data to be moved, and move them quickly as well. + +================================================================================ +KEY FEATURES +================================================================================ + +Flash Awareness +--------------- +- Enlarge the random write area for better performance, but provide the high + spatial locality +- Align FS data structures to the operational units in FTL as best efforts + +Wandering Tree Problem +---------------------- +- Use a term, “node”, that represents inodes as well as various pointer blocks +- Introduce Node Address Table (NAT) containing the locations of all the “node” + blocks; this will cut off the update propagation. + +Cleaning Overhead +----------------- +- Support a background cleaning process +- Support greedy and cost-benefit algorithms for victim selection policies +- Support multi-head logs for static/dynamic hot and cold data separation +- Introduce adaptive logging for efficient block allocation + +================================================================================ +MOUNT OPTIONS +================================================================================ + +background_gc=%s Turn on/off cleaning operations, namely garbage + collection, triggered in background when I/O subsystem is + idle. If background_gc=on, it will turn on the garbage + collection and if background_gc=off, garbage collection + will be truned off. + Default value for this option is on. So garbage + collection is on by default. +disable_roll_forward Disable the roll-forward recovery routine +norecovery Disable the roll-forward recovery routine, mounted read- + only (i.e., -o ro,disable_roll_forward) +discard Issue discard/TRIM commands when a segment is cleaned. +no_heap Disable heap-style segment allocation which finds free + segments for data from the beginning of main area, while + for node from the end of main area. +nouser_xattr Disable Extended User Attributes. Note: xattr is enabled + by default if CONFIG_F2FS_FS_XATTR is selected. +noacl Disable POSIX Access Control List. Note: acl is enabled + by default if CONFIG_F2FS_FS_POSIX_ACL is selected. +active_logs=%u Support configuring the number of active logs. In the + current design, f2fs supports only 2, 4, and 6 logs. + Default number is 6. +disable_ext_identify Disable the extension list configured by mkfs, so f2fs + does not aware of cold files such as media files. +inline_xattr Enable the inline xattrs feature. +inline_data Enable the inline data feature: New created small(<~3.4k) + files can be written into inode block. +inline_dentry Enable the inline dir feature: data in new created + directory entries can be written into inode block. The + space of inode block which is used to store inline + dentries is limited to ~3.4k. +flush_merge Merge concurrent cache_flush commands as much as possible + to eliminate redundant command issues. If the underlying + device handles the cache_flush command relatively slowly, + recommend to enable this option. +nobarrier This option can be used if underlying storage guarantees + its cached data should be written to the novolatile area. + If this option is set, no cache_flush commands are issued + but f2fs still guarantees the write ordering of all the + data writes. +fastboot This option is used when a system wants to reduce mount + time as much as possible, even though normal performance + can be sacrificed. + +================================================================================ +DEBUGFS ENTRIES +================================================================================ + +/sys/kernel/debug/f2fs/ contains information about all the partitions mounted as +f2fs. Each file shows the whole f2fs information. + +/sys/kernel/debug/f2fs/status includes: + - major file system information managed by f2fs currently + - average SIT information about whole segments + - current memory footprint consumed by f2fs. + +================================================================================ +SYSFS ENTRIES +================================================================================ + +Information about mounted f2f2 file systems can be found in +/sys/fs/f2fs. Each mounted filesystem will have a directory in +/sys/fs/f2fs based on its device name (i.e., /sys/fs/f2fs/sda). +The files in each per-device directory are shown in table below. + +Files in /sys/fs/f2fs/ +(see also Documentation/ABI/testing/sysfs-fs-f2fs) +.............................................................................. + File Content + + gc_max_sleep_time This tuning parameter controls the maximum sleep + time for the garbage collection thread. Time is + in milliseconds. + + gc_min_sleep_time This tuning parameter controls the minimum sleep + time for the garbage collection thread. Time is + in milliseconds. + + gc_no_gc_sleep_time This tuning parameter controls the default sleep + time for the garbage collection thread. Time is + in milliseconds. + + gc_idle This parameter controls the selection of victim + policy for garbage collection. Setting gc_idle = 0 + (default) will disable this option. Setting + gc_idle = 1 will select the Cost Benefit approach + & setting gc_idle = 2 will select the greedy aproach. + + reclaim_segments This parameter controls the number of prefree + segments to be reclaimed. If the number of prefree + segments is larger than this number, f2fs tries to + conduct checkpoint to reclaim the prefree segments + to free segments. By default, 100 segments, 200MB. + + max_small_discards This parameter controls the number of discard + commands that consist small blocks less than 2MB. + The candidates to be discarded are cached until + checkpoint is triggered, and issued during the + checkpoint. By default, it is disabled with 0. + + trim_sections This parameter controls the number of sections + to be trimmed out in batch mode when FITRIM + conducts. 32 sections is set by default. + + ipu_policy This parameter controls the policy of in-place + updates in f2fs. There are five policies: + 0x01: F2FS_IPU_FORCE, 0x02: F2FS_IPU_SSR, + 0x04: F2FS_IPU_UTIL, 0x08: F2FS_IPU_SSR_UTIL, + 0x10: F2FS_IPU_FSYNC. + + min_ipu_util This parameter controls the threshold to trigger + in-place-updates. The number indicates percentage + of the filesystem utilization, and used by + F2FS_IPU_UTIL and F2FS_IPU_SSR_UTIL policies. + + min_fsync_blocks This parameter controls the threshold to trigger + in-place-updates when F2FS_IPU_FSYNC mode is set. + The number indicates the number of dirty pages + when fsync needs to flush on its call path. If + the number is less than this value, it triggers + in-place-updates. + + max_victim_search This parameter controls the number of trials to + find a victim segment when conducting SSR and + cleaning operations. The default value is 4096 + which covers 8GB block address range. + + dir_level This parameter controls the directory level to + support large directory. If a directory has a + number of files, it can reduce the file lookup + latency by increasing this dir_level value. + Otherwise, it needs to decrease this value to + reduce the space overhead. The default value is 0. + + ram_thresh This parameter controls the memory footprint used + by free nids and cached nat entries. By default, + 10 is set, which indicates 10 MB / 1 GB RAM. + +================================================================================ +USAGE +================================================================================ + +1. Download userland tools and compile them. + +2. Skip, if f2fs was compiled statically inside kernel. + Otherwise, insert the f2fs.ko module. + # insmod f2fs.ko + +3. Create a directory trying to mount + # mkdir /mnt/f2fs + +4. Format the block device, and then mount as f2fs + # mkfs.f2fs -l label /dev/block_device + # mount -t f2fs /dev/block_device /mnt/f2fs + +mkfs.f2fs +--------- +The mkfs.f2fs is for the use of formatting a partition as the f2fs filesystem, +which builds a basic on-disk layout. + +The options consist of: +-l [label] : Give a volume label, up to 512 unicode name. +-a [0 or 1] : Split start location of each area for heap-based allocation. + 1 is set by default, which performs this. +-o [int] : Set overprovision ratio in percent over volume size. + 5 is set by default. +-s [int] : Set the number of segments per section. + 1 is set by default. +-z [int] : Set the number of sections per zone. + 1 is set by default. +-e [str] : Set basic extension list. e.g. "mp3,gif,mov" +-t [0 or 1] : Disable discard command or not. + 1 is set by default, which conducts discard. + +fsck.f2fs +--------- +The fsck.f2fs is a tool to check the consistency of an f2fs-formatted +partition, which examines whether the filesystem metadata and user-made data +are cross-referenced correctly or not. +Note that, initial version of the tool does not fix any inconsistency. + +The options consist of: + -d debug level [default:0] + +dump.f2fs +--------- +The dump.f2fs shows the information of specific inode and dumps SSA and SIT to +file. Each file is dump_ssa and dump_sit. + +The dump.f2fs is used to debug on-disk data structures of the f2fs filesystem. +It shows on-disk inode information reconized by a given inode number, and is +able to dump all the SSA and SIT entries into predefined files, ./dump_ssa and +./dump_sit respectively. + +The options consist of: + -d debug level [default:0] + -i inode no (hex) + -s [SIT dump segno from #1~#2 (decimal), for all 0~-1] + -a [SSA dump segno from #1~#2 (decimal), for all 0~-1] + +Examples: +# dump.f2fs -i [ino] /dev/sdx +# dump.f2fs -s 0~-1 /dev/sdx (SIT dump) +# dump.f2fs -a 0~-1 /dev/sdx (SSA dump) + +================================================================================ +DESIGN +================================================================================ + +On-disk Layout +-------------- + +F2FS divides the whole volume into a number of segments, each of which is fixed +to 2MB in size. A section is composed of consecutive segments, and a zone +consists of a set of sections. By default, section and zone sizes are set to one +segment size identically, but users can easily modify the sizes by mkfs. + +F2FS splits the entire volume into six areas, and all the areas except superblock +consists of multiple segments as described below. + + align with the zone size <-| + |-> align with the segment size + _________________________________________________________________________ + | | | Segment | Node | Segment | | + | Superblock | Checkpoint | Info. | Address | Summary | Main | + | (SB) | (CP) | Table (SIT) | Table (NAT) | Area (SSA) | | + |____________|_____2______|______N______|______N______|______N_____|__N___| + . . + . . + . . + ._________________________________________. + |_Segment_|_..._|_Segment_|_..._|_Segment_| + . . + ._________._________ + |_section_|__...__|_ + . . + .________. + |__zone__| + +- Superblock (SB) + : It is located at the beginning of the partition, and there exist two copies + to avoid file system crash. It contains basic partition information and some + default parameters of f2fs. + +- Checkpoint (CP) + : It contains file system information, bitmaps for valid NAT/SIT sets, orphan + inode lists, and summary entries of current active segments. + +- Segment Information Table (SIT) + : It contains segment information such as valid block count and bitmap for the + validity of all the blocks. + +- Node Address Table (NAT) + : It is composed of a block address table for all the node blocks stored in + Main area. + +- Segment Summary Area (SSA) + : It contains summary entries which contains the owner information of all the + data and node blocks stored in Main area. + +- Main Area + : It contains file and directory data including their indices. + +In order to avoid misalignment between file system and flash-based storage, F2FS +aligns the start block address of CP with the segment size. Also, it aligns the +start block address of Main area with the zone size by reserving some segments +in SSA area. + +Reference the following survey for additional technical details. +https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey + +File System Metadata Structure +------------------------------ + +F2FS adopts the checkpointing scheme to maintain file system consistency. At +mount time, F2FS first tries to find the last valid checkpoint data by scanning +CP area. In order to reduce the scanning time, F2FS uses only two copies of CP. +One of them always indicates the last valid data, which is called as shadow copy +mechanism. In addition to CP, NAT and SIT also adopt the shadow copy mechanism. + +For file system consistency, each CP points to which NAT and SIT copies are +valid, as shown as below. + + +--------+----------+---------+ + | CP | SIT | NAT | + +--------+----------+---------+ + . . . . + . . . . + . . . . + +-------+-------+--------+--------+--------+--------+ + | CP #0 | CP #1 | SIT #0 | SIT #1 | NAT #0 | NAT #1 | + +-------+-------+--------+--------+--------+--------+ + | ^ ^ + | | | + `----------------------------------------' + +Index Structure +--------------- + +The key data structure to manage the data locations is a "node". Similar to +traditional file structures, F2FS has three types of node: inode, direct node, +indirect node. F2FS assigns 4KB to an inode block which contains 923 data block +indices, two direct node pointers, two indirect node pointers, and one double +indirect node pointer as described below. One direct node block contains 1018 +data blocks, and one indirect node block contains also 1018 node blocks. Thus, +one inode block (i.e., a file) covers: + + 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB. + + Inode block (4KB) + |- data (923) + |- direct node (2) + | `- data (1018) + |- indirect node (2) + | `- direct node (1018) + | `- data (1018) + `- double indirect node (1) + `- indirect node (1018) + `- direct node (1018) + `- data (1018) + +Note that, all the node blocks are mapped by NAT which means the location of +each node is translated by the NAT table. In the consideration of the wandering +tree problem, F2FS is able to cut off the propagation of node updates caused by +leaf data writes. + +Directory Structure +------------------- + +A directory entry occupies 11 bytes, which consists of the following attributes. + +- hash hash value of the file name +- ino inode number +- len the length of file name +- type file type such as directory, symlink, etc + +A dentry block consists of 214 dentry slots and file names. Therein a bitmap is +used to represent whether each dentry is valid or not. A dentry block occupies +4KB with the following composition. + + Dentry Block(4 K) = bitmap (27 bytes) + reserved (3 bytes) + + dentries(11 * 214 bytes) + file name (8 * 214 bytes) + + [Bucket] + +--------------------------------+ + |dentry block 1 | dentry block 2 | + +--------------------------------+ + . . + . . + . [Dentry Block Structure: 4KB] . + +--------+----------+----------+------------+ + | bitmap | reserved | dentries | file names | + +--------+----------+----------+------------+ + [Dentry Block: 4KB] . . + . . + . . + +------+------+-----+------+ + | hash | ino | len | type | + +------+------+-----+------+ + [Dentry Structure: 11 bytes] + +F2FS implements multi-level hash tables for directory structure. Each level has +a hash table with dedicated number of hash buckets as shown below. Note that +"A(2B)" means a bucket includes 2 data blocks. + +---------------------- +A : bucket +B : block +N : MAX_DIR_HASH_DEPTH +---------------------- + +level #0 | A(2B) + | +level #1 | A(2B) - A(2B) + | +level #2 | A(2B) - A(2B) - A(2B) - A(2B) + . | . . . . +level #N/2 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) + . | . . . . +level #N | A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B) + +The number of blocks and buckets are determined by, + + ,- 2, if n < MAX_DIR_HASH_DEPTH / 2, + # of blocks in level #n = | + `- 4, Otherwise + + ,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2, + # of buckets in level #n = | + `- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise + +When F2FS finds a file name in a directory, at first a hash value of the file +name is calculated. Then, F2FS scans the hash table in level #0 to find the +dentry consisting of the file name and its inode number. If not found, F2FS +scans the next hash table in level #1. In this way, F2FS scans hash tables in +each levels incrementally from 1 to N. In each levels F2FS needs to scan only +one bucket determined by the following equation, which shows O(log(# of files)) +complexity. + + bucket number to scan in level #n = (hash value) % (# of buckets in level #n) + +In the case of file creation, F2FS finds empty consecutive slots that cover the +file name. F2FS searches the empty slots in the hash tables of whole levels from +1 to N in the same way as the lookup operation. + +The following figure shows an example of two cases holding children. + --------------> Dir <-------------- + | | + child child + + child - child [hole] - child + + child - child - child [hole] - [hole] - child + + Case 1: Case 2: + Number of children = 6, Number of children = 3, + File size = 7 File size = 7 + +Default Block Allocation +------------------------ + +At runtime, F2FS manages six active logs inside "Main" area: Hot/Warm/Cold node +and Hot/Warm/Cold data. + +- Hot node contains direct node blocks of directories. +- Warm node contains direct node blocks except hot node blocks. +- Cold node contains indirect node blocks +- Hot data contains dentry blocks +- Warm data contains data blocks except hot and cold data blocks +- Cold data contains multimedia data or migrated data blocks + +LFS has two schemes for free space management: threaded log and copy-and-compac- +tion. The copy-and-compaction scheme which is known as cleaning, is well-suited +for devices showing very good sequential write performance, since free segments +are served all the time for writing new data. However, it suffers from cleaning +overhead under high utilization. Contrarily, the threaded log scheme suffers +from random writes, but no cleaning process is needed. F2FS adopts a hybrid +scheme where the copy-and-compaction scheme is adopted by default, but the +policy is dynamically changed to the threaded log scheme according to the file +system status. + +In order to align F2FS with underlying flash-based storage, F2FS allocates a +segment in a unit of section. F2FS expects that the section size would be the +same as the unit size of garbage collection in FTL. Furthermore, with respect +to the mapping granularity in FTL, F2FS allocates each section of the active +logs from different zones as much as possible, since FTL can write the data in +the active logs into one allocation unit according to its mapping granularity. + +Cleaning process +---------------- + +F2FS does cleaning both on demand and in the background. On-demand cleaning is +triggered when there are not enough free segments to serve VFS calls. Background +cleaner is operated by a kernel thread, and triggers the cleaning job when the +system is idle. + +F2FS supports two victim selection policies: greedy and cost-benefit algorithms. +In the greedy algorithm, F2FS selects a victim segment having the smallest number +of valid blocks. In the cost-benefit algorithm, F2FS selects a victim segment +according to the segment age and the number of valid blocks in order to address +log block thrashing problem in the greedy algorithm. F2FS adopts the greedy +algorithm for on-demand cleaner, while background cleaner adopts cost-benefit +algorithm. + +In order to identify whether the data in the victim segment are valid or not, +F2FS manages a bitmap. Each bit represents the validity of a block, and the +bitmap is composed of a bit stream covering whole blocks in main area. diff --git a/fs/Kconfig b/fs/Kconfig index 1dd49481854d..52ee3ef886d5 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -224,6 +224,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" +source "fs/f2fs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index 95cf9de6ae02..cf508e5240be 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -122,6 +122,7 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ +obj-$(CONFIG_F2FS_FS) += f2fs/ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig new file mode 100644 index 000000000000..05f0f663f14c --- /dev/null +++ b/fs/f2fs/Kconfig @@ -0,0 +1,83 @@ +config F2FS_FS + tristate "F2FS filesystem support" + depends on BLOCK + help + F2FS is based on Log-structured File System (LFS), which supports + versatile "flash-friendly" features. The design has been focused on + addressing the fundamental issues in LFS, which are snowball effect + of wandering tree and high cleaning overhead. + + Since flash-based storages show different characteristics according to + the internal geometry or flash memory management schemes aka FTL, F2FS + and tools support various parameters not only for configuring on-disk + layout, but also for selecting allocation and cleaning algorithms. + + If unsure, say N. + +config F2FS_STAT_FS + bool "F2FS Status Information" + depends on F2FS_FS && DEBUG_FS + default y + help + /sys/kernel/debug/f2fs/ contains information about all the partitions + mounted as f2fs. Each file shows the whole f2fs information. + + /sys/kernel/debug/f2fs/status includes: + - major filesystem information managed by f2fs currently + - average SIT information about whole segments + - current memory footprint consumed by f2fs. + +config F2FS_FS_XATTR + bool "F2FS extended attributes" + depends on F2FS_FS + default y + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + +config F2FS_FS_POSIX_ACL + bool "F2FS Access Control Lists" + depends on F2FS_FS_XATTR + select FS_POSIX_ACL + default y + help + Posix Access Control Lists (ACLs) support permissions for users and + gourps beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the POSIX ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config F2FS_FS_SECURITY + bool "F2FS Security Labels" + depends on F2FS_FS_XATTR + help + Security labels provide an access control facility to support Linux + Security Models (LSMs) accepted by AppArmor, SELinux, Smack and TOMOYO + Linux. This option enables an extended attribute handler for file + security labels in the f2fs filesystem, so that it requires enabling + the extended attribute support in advance. + + If you are not using a security module, say N. + +config F2FS_CHECK_FS + bool "F2FS consistency checking feature" + depends on F2FS_FS + help + Enables BUG_ONs which check the filesystem consistency in runtime. + + If you want to improve the performance, say N. + +config F2FS_IO_TRACE + bool "F2FS IO tracer" + depends on F2FS_FS + depends on FUNCTION_TRACER + help + F2FS IO trace is based on a function trace, which gathers process + information and block IO patterns in the filesystem level. + + If unsure, say N. diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile new file mode 100644 index 000000000000..d92397731db8 --- /dev/null +++ b/fs/f2fs/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_F2FS_FS) += f2fs.o + +f2fs-y := dir.o file.o inode.o namei.o hash.o super.o inline.o +f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o +f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o +f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o +f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o +f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c new file mode 100644 index 000000000000..df1a307f5e9c --- /dev/null +++ b/fs/f2fs/acl.c @@ -0,0 +1,403 @@ +/* + * fs/f2fs/acl.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Portions of this code from linux/fs/ext2/acl.c + * + * Copyright (C) 2001-2003 Andreas Gruenbacher, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include "f2fs.h" +#include "xattr.h" +#include "acl.h" + +static inline size_t f2fs_acl_size(int count) +{ + if (count <= 4) { + return sizeof(struct f2fs_acl_header) + + count * sizeof(struct f2fs_acl_entry_short); + } else { + return sizeof(struct f2fs_acl_header) + + 4 * sizeof(struct f2fs_acl_entry_short) + + (count - 4) * sizeof(struct f2fs_acl_entry); + } +} + +static inline int f2fs_acl_count(size_t size) +{ + ssize_t s; + size -= sizeof(struct f2fs_acl_header); + s = size - 4 * sizeof(struct f2fs_acl_entry_short); + if (s < 0) { + if (size % sizeof(struct f2fs_acl_entry_short)) + return -1; + return size / sizeof(struct f2fs_acl_entry_short); + } else { + if (s % sizeof(struct f2fs_acl_entry)) + return -1; + return s / sizeof(struct f2fs_acl_entry) + 4; + } +} + +static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) +{ + int i, count; + struct posix_acl *acl; + struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; + struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); + const char *end = value + size; + + if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) + return ERR_PTR(-EINVAL); + + count = f2fs_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_NOFS); + if (!acl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < count; i++) { + + if ((char *)entry > end) + goto fail; + + acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); + acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); + + switch (acl->a_entries[i].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + entry = (struct f2fs_acl_entry *)((char *)entry + + sizeof(struct f2fs_acl_entry_short)); + break; + + case ACL_USER: + case ACL_GROUP: + acl->a_entries[i].e_id = le32_to_cpu(entry->e_id); + entry = (struct f2fs_acl_entry *)((char *)entry + + sizeof(struct f2fs_acl_entry)); + break; + default: + goto fail; + } + } + if ((char *)entry != end) + goto fail; + return acl; +fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) +{ + struct f2fs_acl_header *f2fs_acl; + struct f2fs_acl_entry *entry; + int i; + + f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * + sizeof(struct f2fs_acl_entry), GFP_NOFS); + if (!f2fs_acl) + return ERR_PTR(-ENOMEM); + + f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION); + entry = (struct f2fs_acl_entry *)(f2fs_acl + 1); + + for (i = 0; i < acl->a_count; i++) { + + entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag); + entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm); + + switch (acl->a_entries[i].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = cpu_to_le32(acl->a_entries[i].e_id); + entry = (struct f2fs_acl_entry *)((char *)entry + + sizeof(struct f2fs_acl_entry)); + break; + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + entry = (struct f2fs_acl_entry *)((char *)entry + + sizeof(struct f2fs_acl_entry_short)); + break; + default: + goto fail; + } + } + *size = f2fs_acl_size(acl->a_count); + return (void *)f2fs_acl; + +fail: + kfree(f2fs_acl); + return ERR_PTR(-EINVAL); +} + +static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type, + struct page *dpage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; + void *value = NULL; + struct posix_acl *acl; + int retval; + + if (!test_opt(sbi, POSIX_ACL)) + return NULL; + + acl = get_cached_acl(inode, type); + if (acl != ACL_NOT_CACHED) + return acl; + + if (type == ACL_TYPE_ACCESS) + name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; + + retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage); + if (retval > 0) { + value = kmalloc(retval, GFP_F2FS_ZERO); + if (!value) + return ERR_PTR(-ENOMEM); + retval = f2fs_getxattr(inode, name_index, "", value, + retval, dpage); + } + + if (retval > 0) + acl = f2fs_acl_from_disk(value, retval); + else if (retval == -ENODATA) + acl = NULL; + else + acl = ERR_PTR(retval); + kfree(value); + + if (!IS_ERR(acl)) + set_cached_acl(inode, type, acl); + + return acl; +} + +struct posix_acl *f2fs_get_acl(struct inode *inode, int type) +{ + return __f2fs_get_acl(inode, type, NULL); +} + +static int f2fs_set_acl(struct inode *inode, int type, + struct posix_acl *acl, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct f2fs_inode_info *fi = F2FS_I(inode); + int name_index; + void *value = NULL; + size_t size = 0; + int error; + + if (!test_opt(sbi, POSIX_ACL)) + return 0; + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch (type) { + case ACL_TYPE_ACCESS: + name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; + if (acl) { + error = posix_acl_equiv_mode(acl, &inode->i_mode); + if (error < 0) + return error; + set_acl_inode(fi, inode->i_mode); + if (error == 0) + acl = NULL; + } + break; + + case ACL_TYPE_DEFAULT: + name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + + default: + return -EINVAL; + } + + if (acl) { + value = f2fs_acl_to_disk(acl, &size); + if (IS_ERR(value)) { + clear_inode_flag(fi, FI_ACL_MODE); + return (int)PTR_ERR(value); + } + } + + error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0); + + kfree(value); + if (!error) + set_cached_acl(inode, type, acl); + + clear_inode_flag(fi, FI_ACL_MODE); + return error; +} + +int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage, + struct page *dpage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); + struct posix_acl *acl = NULL; + int error = 0; + + if (!S_ISLNK(inode->i_mode)) { + if (test_opt(sbi, POSIX_ACL)) { + acl = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage); + if (IS_ERR(acl)) + return PTR_ERR(acl); + } + if (!acl) + inode->i_mode &= ~current_umask(); + } + + if (!test_opt(sbi, POSIX_ACL) || !acl) + goto cleanup; + + if (S_ISDIR(inode->i_mode)) { + error = f2fs_set_acl(inode, ACL_TYPE_DEFAULT, acl, ipage); + if (error) + goto cleanup; + } + error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); + if (error < 0) + return error; + if (error > 0) + error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl, ipage); +cleanup: + posix_acl_release(acl); + return error; +} + +int f2fs_acl_chmod(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct posix_acl *acl; + int error; + umode_t mode = get_inode_mode(inode); + + if (!test_opt(sbi, POSIX_ACL)) + return 0; + if (S_ISLNK(mode)) + return -EOPNOTSUPP; + + acl = f2fs_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + + error = posix_acl_chmod(&acl, GFP_KERNEL, mode); + if (error) + return error; + + error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl, NULL); + posix_acl_release(acl); + return error; +} + +static size_t f2fs_xattr_list_acl(struct dentry *dentry, char *list, + size_t list_size, const char *name, size_t name_len, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + const char *xname = POSIX_ACL_XATTR_DEFAULT; + size_t size; + + if (!test_opt(sbi, POSIX_ACL)) + return 0; + + if (type == ACL_TYPE_ACCESS) + xname = POSIX_ACL_XATTR_ACCESS; + + size = strlen(xname) + 1; + if (list && size <= list_size) + memcpy(list, xname, size); + return size; +} + +static int f2fs_xattr_get_acl(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + struct posix_acl *acl; + int error; + + if (strcmp(name, "") != 0) + return -EINVAL; + if (!test_opt(sbi, POSIX_ACL)) + return -EOPNOTSUPP; + + acl = f2fs_get_acl(dentry->d_inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + return -ENODATA; + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return error; +} + +static int f2fs_xattr_set_acl(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + struct posix_acl *acl = NULL; + int error; + + if (strcmp(name, "") != 0) + return -EINVAL; + if (!test_opt(sbi, POSIX_ACL)) + return -EOPNOTSUPP; + if (!inode_owner_or_capable(inode)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + error = posix_acl_valid(acl); + if (error) + goto release_and_out; + } + } else { + acl = NULL; + } + + error = f2fs_set_acl(inode, type, acl, NULL); + +release_and_out: + posix_acl_release(acl); + return error; +} + +const struct xattr_handler f2fs_xattr_acl_default_handler = { + .prefix = POSIX_ACL_XATTR_DEFAULT, + .flags = ACL_TYPE_DEFAULT, + .list = f2fs_xattr_list_acl, + .get = f2fs_xattr_get_acl, + .set = f2fs_xattr_set_acl, +}; + +const struct xattr_handler f2fs_xattr_acl_access_handler = { + .prefix = POSIX_ACL_XATTR_ACCESS, + .flags = ACL_TYPE_ACCESS, + .list = f2fs_xattr_list_acl, + .get = f2fs_xattr_get_acl, + .set = f2fs_xattr_set_acl, +}; diff --git a/fs/f2fs/acl.h b/fs/f2fs/acl.h new file mode 100644 index 000000000000..b4ba6866822e --- /dev/null +++ b/fs/f2fs/acl.h @@ -0,0 +1,59 @@ +/* + * fs/f2fs/acl.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Portions of this code from linux/fs/ext2/acl.h + * + * Copyright (C) 2001-2003 Andreas Gruenbacher, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __F2FS_ACL_H__ +#define __F2FS_ACL_H__ + +#include + +#define F2FS_ACL_VERSION 0x0001 + +struct f2fs_acl_entry { + __le16 e_tag; + __le16 e_perm; + __le32 e_id; +}; + +struct f2fs_acl_entry_short { + __le16 e_tag; + __le16 e_perm; +}; + +struct f2fs_acl_header { + __le32 a_version; +}; + +#ifdef CONFIG_F2FS_FS_POSIX_ACL + +extern struct posix_acl *f2fs_get_acl(struct inode *, int); +extern int f2fs_acl_chmod(struct inode *); +extern int f2fs_init_acl(struct inode *, struct inode *, struct page *, + struct page *); +#else +#define f2fs_check_acl NULL +#define f2fs_get_acl NULL +#define f2fs_set_acl NULL + +static inline int f2fs_acl_chmod(struct inode *inode) +{ + return 0; +} + +static inline int f2fs_init_acl(struct inode *inode, struct inode *dir, + struct page *ipage, struct page *dpage) +{ + return 0; +} +#endif +#endif /* __F2FS_ACL_H__ */ diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c new file mode 100644 index 000000000000..7b20cf064338 --- /dev/null +++ b/fs/f2fs/checkpoint.c @@ -0,0 +1,1136 @@ +/* + * fs/f2fs/checkpoint.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "trace.h" +#include + +static struct kmem_cache *ino_entry_slab; +struct kmem_cache *inode_entry_slab; + +/* + * We guarantee no failure on the returned page. + */ +struct page *grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) +{ + struct address_space *mapping = META_MAPPING(sbi); + struct page *page = NULL; +repeat: + page = grab_cache_page(mapping, index); + if (!page) { + cond_resched(); + goto repeat; + } + f2fs_wait_on_page_writeback(page, META); + SetPageUptodate(page); + return page; +} + +/* + * We guarantee no failure on the returned page. + */ +struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) +{ + struct address_space *mapping = META_MAPPING(sbi); + struct page *page; + struct f2fs_io_info fio = { + .type = META, + .rw = READ_SYNC | REQ_META | REQ_PRIO, + .blk_addr = index, + }; +repeat: + page = grab_cache_page(mapping, index); + if (!page) { + cond_resched(); + goto repeat; + } + if (PageUptodate(page)) + goto out; + + if (f2fs_submit_page_bio(sbi, page, &fio)) + goto repeat; + + lock_page(page); + if (unlikely(page->mapping != mapping)) { + f2fs_put_page(page, 1); + goto repeat; + } +out: + mark_page_accessed(page); + return page; +} + +static inline bool is_valid_blkaddr(struct f2fs_sb_info *sbi, + block_t blkaddr, int type) +{ + switch (type) { + case META_NAT: + break; + case META_SIT: + if (unlikely(blkaddr >= SIT_BLK_CNT(sbi))) + return false; + break; + case META_SSA: + if (unlikely(blkaddr >= MAIN_BLKADDR(sbi) || + blkaddr < SM_I(sbi)->ssa_blkaddr)) + return false; + break; + case META_CP: + if (unlikely(blkaddr >= SIT_I(sbi)->sit_base_addr || + blkaddr < __start_cp_addr(sbi))) + return false; + break; + case META_POR: + if (unlikely(blkaddr >= MAX_BLKADDR(sbi) || + blkaddr < MAIN_BLKADDR(sbi))) + return false; + break; + default: + BUG(); + } + + return true; +} + +/* + * Readahead CP/NAT/SIT/SSA pages + */ +int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type) +{ + block_t prev_blk_addr = 0; + struct page *page; + block_t blkno = start; + struct f2fs_io_info fio = { + .type = META, + .rw = READ_SYNC | REQ_META | REQ_PRIO + }; + + for (; nrpages-- > 0; blkno++) { + + if (!is_valid_blkaddr(sbi, blkno, type)) + goto out; + + switch (type) { + case META_NAT: + if (unlikely(blkno >= + NAT_BLOCK_OFFSET(NM_I(sbi)->max_nid))) + blkno = 0; + /* get nat block addr */ + fio.blk_addr = current_nat_addr(sbi, + blkno * NAT_ENTRY_PER_BLOCK); + break; + case META_SIT: + /* get sit block addr */ + fio.blk_addr = current_sit_addr(sbi, + blkno * SIT_ENTRY_PER_BLOCK); + if (blkno != start && prev_blk_addr + 1 != fio.blk_addr) + goto out; + prev_blk_addr = fio.blk_addr; + break; + case META_SSA: + case META_CP: + case META_POR: + fio.blk_addr = blkno; + break; + default: + BUG(); + } + + page = grab_cache_page(META_MAPPING(sbi), fio.blk_addr); + if (!page) + continue; + if (PageUptodate(page)) { + f2fs_put_page(page, 1); + continue; + } + + f2fs_submit_page_mbio(sbi, page, &fio); + f2fs_put_page(page, 0); + } +out: + f2fs_submit_merged_bio(sbi, META, READ); + return blkno - start; +} + +void ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index) +{ + struct page *page; + bool readahead = false; + + page = find_get_page(META_MAPPING(sbi), index); + if (!page || (page && !PageUptodate(page))) + readahead = true; + f2fs_put_page(page, 0); + + if (readahead) + ra_meta_pages(sbi, index, MAX_BIO_BLOCKS(sbi), META_POR); +} + +static int f2fs_write_meta_page(struct page *page, + struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(page); + + trace_f2fs_writepage(page, META); + + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (wbc->for_reclaim && page->index < GET_SUM_BLOCK(sbi, 0)) + goto redirty_out; + if (unlikely(f2fs_cp_error(sbi))) + goto redirty_out; + + f2fs_wait_on_page_writeback(page, META); + write_meta_page(sbi, page); + dec_page_count(sbi, F2FS_DIRTY_META); + unlock_page(page); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio(sbi, META, WRITE); + return 0; + +redirty_out: + redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; +} + +static int f2fs_write_meta_pages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + long diff, written; + + trace_f2fs_writepages(mapping->host, wbc, META); + + /* collect a number of dirty meta pages and write together */ + if (wbc->for_kupdate || + get_pages(sbi, F2FS_DIRTY_META) < nr_pages_to_skip(sbi, META)) + goto skip_write; + + /* if mounting is failed, skip writing node pages */ + mutex_lock(&sbi->cp_mutex); + diff = nr_pages_to_write(sbi, META, wbc); + written = sync_meta_pages(sbi, META, wbc->nr_to_write); + mutex_unlock(&sbi->cp_mutex); + wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); + return 0; + +skip_write: + wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_META); + return 0; +} + +long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, + long nr_to_write) +{ + struct address_space *mapping = META_MAPPING(sbi); + pgoff_t index = 0, end = LONG_MAX; + struct pagevec pvec; + long nwritten = 0; + struct writeback_control wbc = { + .for_reclaim = 0, + }; + + pagevec_init(&pvec, 0); + + while (index <= end) { + int i, nr_pages; + nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); + if (unlikely(nr_pages == 0)) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + lock_page(page); + + if (unlikely(page->mapping != mapping)) { +continue_unlock: + unlock_page(page); + continue; + } + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + if (!clear_page_dirty_for_io(page)) + goto continue_unlock; + + if (mapping->a_ops->writepage(page, &wbc)) { + unlock_page(page); + break; + } + nwritten++; + if (unlikely(nwritten >= nr_to_write)) + break; + } + pagevec_release(&pvec); + cond_resched(); + } + + if (nwritten) + f2fs_submit_merged_bio(sbi, type, WRITE); + + return nwritten; +} + +static int f2fs_set_meta_page_dirty(struct page *page) +{ + trace_f2fs_set_page_dirty(page, META); + + SetPageUptodate(page); + if (!PageDirty(page)) { + __set_page_dirty_nobuffers(page); + inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_META); + SetPagePrivate(page); + f2fs_trace_pid(page); + return 1; + } + return 0; +} + +const struct address_space_operations f2fs_meta_aops = { + .writepage = f2fs_write_meta_page, + .writepages = f2fs_write_meta_pages, + .set_page_dirty = f2fs_set_meta_page_dirty, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, +}; + +static void __add_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) +{ + struct inode_management *im = &sbi->im[type]; + struct ino_entry *e; +retry: + if (radix_tree_preload(GFP_NOFS)) { + cond_resched(); + goto retry; + } + + spin_lock(&im->ino_lock); + + e = radix_tree_lookup(&im->ino_root, ino); + if (!e) { + e = kmem_cache_alloc(ino_entry_slab, GFP_ATOMIC); + if (!e) { + spin_unlock(&im->ino_lock); + radix_tree_preload_end(); + goto retry; + } + if (radix_tree_insert(&im->ino_root, ino, e)) { + spin_unlock(&im->ino_lock); + kmem_cache_free(ino_entry_slab, e); + radix_tree_preload_end(); + goto retry; + } + memset(e, 0, sizeof(struct ino_entry)); + e->ino = ino; + + list_add_tail(&e->list, &im->ino_list); + if (type != ORPHAN_INO) + im->ino_num++; + } + spin_unlock(&im->ino_lock); + radix_tree_preload_end(); +} + +static void __remove_ino_entry(struct f2fs_sb_info *sbi, nid_t ino, int type) +{ + struct inode_management *im = &sbi->im[type]; + struct ino_entry *e; + + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); + if (e) { + list_del(&e->list); + radix_tree_delete(&im->ino_root, ino); + im->ino_num--; + spin_unlock(&im->ino_lock); + kmem_cache_free(ino_entry_slab, e); + return; + } + spin_unlock(&im->ino_lock); +} + +void add_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) +{ + /* add new dirty ino entry into list */ + __add_ino_entry(sbi, ino, type); +} + +void remove_dirty_inode(struct f2fs_sb_info *sbi, nid_t ino, int type) +{ + /* remove dirty ino entry from list */ + __remove_ino_entry(sbi, ino, type); +} + +/* mode should be APPEND_INO or UPDATE_INO */ +bool exist_written_data(struct f2fs_sb_info *sbi, nid_t ino, int mode) +{ + struct inode_management *im = &sbi->im[mode]; + struct ino_entry *e; + + spin_lock(&im->ino_lock); + e = radix_tree_lookup(&im->ino_root, ino); + spin_unlock(&im->ino_lock); + return e ? true : false; +} + +void release_dirty_inode(struct f2fs_sb_info *sbi) +{ + struct ino_entry *e, *tmp; + int i; + + for (i = APPEND_INO; i <= UPDATE_INO; i++) { + struct inode_management *im = &sbi->im[i]; + + spin_lock(&im->ino_lock); + list_for_each_entry_safe(e, tmp, &im->ino_list, list) { + list_del(&e->list); + radix_tree_delete(&im->ino_root, e->ino); + kmem_cache_free(ino_entry_slab, e); + im->ino_num--; + } + spin_unlock(&im->ino_lock); + } +} + +int acquire_orphan_inode(struct f2fs_sb_info *sbi) +{ + struct inode_management *im = &sbi->im[ORPHAN_INO]; + int err = 0; + + spin_lock(&im->ino_lock); + if (unlikely(im->ino_num >= sbi->max_orphans)) + err = -ENOSPC; + else + im->ino_num++; + spin_unlock(&im->ino_lock); + + return err; +} + +void release_orphan_inode(struct f2fs_sb_info *sbi) +{ + struct inode_management *im = &sbi->im[ORPHAN_INO]; + + spin_lock(&im->ino_lock); + f2fs_bug_on(sbi, im->ino_num == 0); + im->ino_num--; + spin_unlock(&im->ino_lock); +} + +void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) +{ + /* add new orphan ino entry into list */ + __add_ino_entry(sbi, ino, ORPHAN_INO); +} + +void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) +{ + /* remove orphan entry from orphan list */ + __remove_ino_entry(sbi, ino, ORPHAN_INO); +} + +static void recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct inode *inode = f2fs_iget(sbi->sb, ino); + f2fs_bug_on(sbi, IS_ERR(inode)); + clear_nlink(inode); + + /* truncate all the data during iput */ + iput(inode); +} + +void recover_orphan_inodes(struct f2fs_sb_info *sbi) +{ + block_t start_blk, orphan_blocks, i, j; + + if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) + return; + + set_sbi_flag(sbi, SBI_POR_DOING); + + start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); + orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi); + + ra_meta_pages(sbi, start_blk, orphan_blocks, META_CP); + + for (i = 0; i < orphan_blocks; i++) { + struct page *page = get_meta_page(sbi, start_blk + i); + struct f2fs_orphan_block *orphan_blk; + + orphan_blk = (struct f2fs_orphan_block *)page_address(page); + for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { + nid_t ino = le32_to_cpu(orphan_blk->ino[j]); + recover_orphan_inode(sbi, ino); + } + f2fs_put_page(page, 1); + } + /* clear Orphan Flag */ + clear_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG); + clear_sbi_flag(sbi, SBI_POR_DOING); + return; +} + +static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) +{ + struct list_head *head; + struct f2fs_orphan_block *orphan_blk = NULL; + unsigned int nentries = 0; + unsigned short index; + unsigned short orphan_blocks; + struct page *page = NULL; + struct ino_entry *orphan = NULL; + struct inode_management *im = &sbi->im[ORPHAN_INO]; + + orphan_blocks = GET_ORPHAN_BLOCKS(im->ino_num); + + for (index = 0; index < orphan_blocks; index++) + grab_meta_page(sbi, start_blk + index); + + index = 1; + spin_lock(&im->ino_lock); + head = &im->ino_list; + + /* loop for each orphan inode entry and write them in Jornal block */ + list_for_each_entry(orphan, head, list) { + if (!page) { + page = find_get_page(META_MAPPING(sbi), start_blk++); + f2fs_bug_on(sbi, !page); + orphan_blk = + (struct f2fs_orphan_block *)page_address(page); + memset(orphan_blk, 0, sizeof(*orphan_blk)); + f2fs_put_page(page, 0); + } + + orphan_blk->ino[nentries++] = cpu_to_le32(orphan->ino); + + if (nentries == F2FS_ORPHANS_PER_BLOCK) { + /* + * an orphan block is full of 1020 entries, + * then we need to flush current orphan blocks + * and bring another one in memory + */ + orphan_blk->blk_addr = cpu_to_le16(index); + orphan_blk->blk_count = cpu_to_le16(orphan_blocks); + orphan_blk->entry_count = cpu_to_le32(nentries); + set_page_dirty(page); + f2fs_put_page(page, 1); + index++; + nentries = 0; + page = NULL; + } + } + + if (page) { + orphan_blk->blk_addr = cpu_to_le16(index); + orphan_blk->blk_count = cpu_to_le16(orphan_blocks); + orphan_blk->entry_count = cpu_to_le32(nentries); + set_page_dirty(page); + f2fs_put_page(page, 1); + } + + spin_unlock(&im->ino_lock); +} + +static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, + block_t cp_addr, unsigned long long *version) +{ + struct page *cp_page_1, *cp_page_2 = NULL; + unsigned long blk_size = sbi->blocksize; + struct f2fs_checkpoint *cp_block; + unsigned long long cur_version = 0, pre_version = 0; + size_t crc_offset; + __u32 crc = 0; + + /* Read the 1st cp block in this CP pack */ + cp_page_1 = get_meta_page(sbi, cp_addr); + + /* get the version number */ + cp_block = (struct f2fs_checkpoint *)page_address(cp_page_1); + crc_offset = le32_to_cpu(cp_block->checksum_offset); + if (crc_offset >= blk_size) + goto invalid_cp1; + + crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); + if (!f2fs_crc_valid(crc, cp_block, crc_offset)) + goto invalid_cp1; + + pre_version = cur_cp_version(cp_block); + + /* Read the 2nd cp block in this CP pack */ + cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; + cp_page_2 = get_meta_page(sbi, cp_addr); + + cp_block = (struct f2fs_checkpoint *)page_address(cp_page_2); + crc_offset = le32_to_cpu(cp_block->checksum_offset); + if (crc_offset >= blk_size) + goto invalid_cp2; + + crc = le32_to_cpu(*((__le32 *)((unsigned char *)cp_block + crc_offset))); + if (!f2fs_crc_valid(crc, cp_block, crc_offset)) + goto invalid_cp2; + + cur_version = cur_cp_version(cp_block); + + if (cur_version == pre_version) { + *version = cur_version; + f2fs_put_page(cp_page_2, 1); + return cp_page_1; + } +invalid_cp2: + f2fs_put_page(cp_page_2, 1); +invalid_cp1: + f2fs_put_page(cp_page_1, 1); + return NULL; +} + +int get_valid_checkpoint(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *cp_block; + struct f2fs_super_block *fsb = sbi->raw_super; + struct page *cp1, *cp2, *cur_page; + unsigned long blk_size = sbi->blocksize; + unsigned long long cp1_version = 0, cp2_version = 0; + unsigned long long cp_start_blk_no; + unsigned int cp_blks = 1 + __cp_payload(sbi); + block_t cp_blk_no; + int i; + + sbi->ckpt = kzalloc(cp_blks * blk_size, GFP_KERNEL); + if (!sbi->ckpt) + return -ENOMEM; + /* + * Finding out valid cp block involves read both + * sets( cp pack1 and cp pack 2) + */ + cp_start_blk_no = le32_to_cpu(fsb->cp_blkaddr); + cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version); + + /* The second checkpoint pack should start at the next segment */ + cp_start_blk_no += ((unsigned long long)1) << + le32_to_cpu(fsb->log_blocks_per_seg); + cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version); + + if (cp1 && cp2) { + if (ver_after(cp2_version, cp1_version)) + cur_page = cp2; + else + cur_page = cp1; + } else if (cp1) { + cur_page = cp1; + } else if (cp2) { + cur_page = cp2; + } else { + goto fail_no_cp; + } + + cp_block = (struct f2fs_checkpoint *)page_address(cur_page); + memcpy(sbi->ckpt, cp_block, blk_size); + + if (cp_blks <= 1) + goto done; + + cp_blk_no = le32_to_cpu(fsb->cp_blkaddr); + if (cur_page == cp2) + cp_blk_no += 1 << le32_to_cpu(fsb->log_blocks_per_seg); + + for (i = 1; i < cp_blks; i++) { + void *sit_bitmap_ptr; + unsigned char *ckpt = (unsigned char *)sbi->ckpt; + + cur_page = get_meta_page(sbi, cp_blk_no + i); + sit_bitmap_ptr = page_address(cur_page); + memcpy(ckpt + i * blk_size, sit_bitmap_ptr, blk_size); + f2fs_put_page(cur_page, 1); + } +done: + f2fs_put_page(cp1, 1); + f2fs_put_page(cp2, 1); + return 0; + +fail_no_cp: + kfree(sbi->ckpt); + return -EINVAL; +} + +static int __add_dirty_inode(struct inode *inode, struct inode_entry *new) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) + return -EEXIST; + + set_inode_flag(F2FS_I(inode), FI_DIRTY_DIR); + F2FS_I(inode)->dirty_dir = new; + list_add_tail(&new->list, &sbi->dir_inode_list); + stat_inc_dirty_dir(sbi); + return 0; +} + +void update_dirty_page(struct inode *inode, struct page *page) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct inode_entry *new; + int ret = 0; + + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) + return; + + if (!S_ISDIR(inode->i_mode)) { + inode_inc_dirty_pages(inode); + goto out; + } + + new = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); + new->inode = inode; + INIT_LIST_HEAD(&new->list); + + spin_lock(&sbi->dir_inode_lock); + ret = __add_dirty_inode(inode, new); + inode_inc_dirty_pages(inode); + spin_unlock(&sbi->dir_inode_lock); + + if (ret) + kmem_cache_free(inode_entry_slab, new); +out: + SetPagePrivate(page); + f2fs_trace_pid(page); +} + +void add_dirty_dir_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct inode_entry *new = + f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); + int ret = 0; + + new->inode = inode; + INIT_LIST_HEAD(&new->list); + + spin_lock(&sbi->dir_inode_lock); + ret = __add_dirty_inode(inode, new); + spin_unlock(&sbi->dir_inode_lock); + + if (ret) + kmem_cache_free(inode_entry_slab, new); +} + +void remove_dirty_dir_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct inode_entry *entry; + + if (!S_ISDIR(inode->i_mode)) + return; + + spin_lock(&sbi->dir_inode_lock); + if (get_dirty_pages(inode) || + !is_inode_flag_set(F2FS_I(inode), FI_DIRTY_DIR)) { + spin_unlock(&sbi->dir_inode_lock); + return; + } + + entry = F2FS_I(inode)->dirty_dir; + list_del(&entry->list); + F2FS_I(inode)->dirty_dir = NULL; + clear_inode_flag(F2FS_I(inode), FI_DIRTY_DIR); + stat_dec_dirty_dir(sbi); + spin_unlock(&sbi->dir_inode_lock); + kmem_cache_free(inode_entry_slab, entry); + + /* Only from the recovery routine */ + if (is_inode_flag_set(F2FS_I(inode), FI_DELAY_IPUT)) { + clear_inode_flag(F2FS_I(inode), FI_DELAY_IPUT); + iput(inode); + } +} + +void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) +{ + struct list_head *head; + struct inode_entry *entry; + struct inode *inode; +retry: + if (unlikely(f2fs_cp_error(sbi))) + return; + + spin_lock(&sbi->dir_inode_lock); + + head = &sbi->dir_inode_list; + if (list_empty(head)) { + spin_unlock(&sbi->dir_inode_lock); + return; + } + entry = list_entry(head->next, struct inode_entry, list); + inode = igrab(entry->inode); + spin_unlock(&sbi->dir_inode_lock); + if (inode) { + filemap_fdatawrite(inode->i_mapping); + iput(inode); + } else { + /* + * We should submit bio, since it exists several + * wribacking dentry pages in the freeing inode. + */ + f2fs_submit_merged_bio(sbi, DATA, WRITE); + cond_resched(); + } + goto retry; +} + +/* + * Freeze all the FS-operations for checkpoint. + */ +static int block_operations(struct f2fs_sb_info *sbi) +{ + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = LONG_MAX, + .for_reclaim = 0, + }; + struct blk_plug plug; + int err = 0; + + blk_start_plug(&plug); + +retry_flush_dents: + f2fs_lock_all(sbi); + /* write all the dirty dentry pages */ + if (get_pages(sbi, F2FS_DIRTY_DENTS)) { + f2fs_unlock_all(sbi); + sync_dirty_dir_inodes(sbi); + if (unlikely(f2fs_cp_error(sbi))) { + err = -EIO; + goto out; + } + goto retry_flush_dents; + } + + /* + * POR: we should ensure that there are no dirty node pages + * until finishing nat/sit flush. + */ +retry_flush_nodes: + down_write(&sbi->node_write); + + if (get_pages(sbi, F2FS_DIRTY_NODES)) { + up_write(&sbi->node_write); + sync_node_pages(sbi, 0, &wbc); + if (unlikely(f2fs_cp_error(sbi))) { + f2fs_unlock_all(sbi); + err = -EIO; + goto out; + } + goto retry_flush_nodes; + } +out: + blk_finish_plug(&plug); + return err; +} + +static void unblock_operations(struct f2fs_sb_info *sbi) +{ + up_write(&sbi->node_write); + f2fs_unlock_all(sbi); +} + +static void wait_on_all_pages_writeback(struct f2fs_sb_info *sbi) +{ + DEFINE_WAIT(wait); + + for (;;) { + prepare_to_wait(&sbi->cp_wait, &wait, TASK_UNINTERRUPTIBLE); + + if (!get_pages(sbi, F2FS_WRITEBACK)) + break; + + io_schedule(); + } + finish_wait(&sbi->cp_wait, &wait); +} + +static void do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned long orphan_num = sbi->im[ORPHAN_INO].ino_num; + nid_t last_nid = nm_i->next_scan_nid; + block_t start_blk; + struct page *cp_page; + unsigned int data_sum_blocks, orphan_blocks; + __u32 crc32 = 0; + void *kaddr; + int i; + int cp_payload_blks = __cp_payload(sbi); + + /* + * This avoids to conduct wrong roll-forward operations and uses + * metapages, so should be called prior to sync_meta_pages below. + */ + discard_next_dnode(sbi, NEXT_FREE_BLKADDR(sbi, curseg)); + + /* Flush all the NAT/SIT pages */ + while (get_pages(sbi, F2FS_DIRTY_META)) { + sync_meta_pages(sbi, META, LONG_MAX); + if (unlikely(f2fs_cp_error(sbi))) + return; + } + + next_free_nid(sbi, &last_nid); + + /* + * modify checkpoint + * version number is already updated + */ + ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi)); + ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi)); + ckpt->free_segment_count = cpu_to_le32(free_segments(sbi)); + for (i = 0; i < NR_CURSEG_NODE_TYPE; i++) { + ckpt->cur_node_segno[i] = + cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_NODE)); + ckpt->cur_node_blkoff[i] = + cpu_to_le16(curseg_blkoff(sbi, i + CURSEG_HOT_NODE)); + ckpt->alloc_type[i + CURSEG_HOT_NODE] = + curseg_alloc_type(sbi, i + CURSEG_HOT_NODE); + } + for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { + ckpt->cur_data_segno[i] = + cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_DATA)); + ckpt->cur_data_blkoff[i] = + cpu_to_le16(curseg_blkoff(sbi, i + CURSEG_HOT_DATA)); + ckpt->alloc_type[i + CURSEG_HOT_DATA] = + curseg_alloc_type(sbi, i + CURSEG_HOT_DATA); + } + + ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); + ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); + ckpt->next_free_nid = cpu_to_le32(last_nid); + + /* 2 cp + n data seg summary + orphan inode blocks */ + data_sum_blocks = npages_for_summary_flush(sbi, false); + if (data_sum_blocks < NR_CURSEG_DATA_TYPE) + set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); + else + clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); + + orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); + ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + + orphan_blocks); + + if (__remain_node_summaries(cpc->reason)) + ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS+ + cp_payload_blks + data_sum_blocks + + orphan_blocks + NR_CURSEG_NODE_TYPE); + else + ckpt->cp_pack_total_block_count = cpu_to_le32(F2FS_CP_PACKS + + cp_payload_blks + data_sum_blocks + + orphan_blocks); + + if (cpc->reason == CP_UMOUNT) + set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + else + clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); + + if (cpc->reason == CP_FASTBOOT) + set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + else + clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); + + if (orphan_num) + set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + else + clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); + + if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) + set_ckpt_flags(ckpt, CP_FSCK_FLAG); + + /* update SIT/NAT bitmap */ + get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); + get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); + + crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset)); + *((__le32 *)((unsigned char *)ckpt + + le32_to_cpu(ckpt->checksum_offset))) + = cpu_to_le32(crc32); + + start_blk = __start_cp_addr(sbi); + + /* write out checkpoint buffer at block 0 */ + cp_page = grab_meta_page(sbi, start_blk++); + kaddr = page_address(cp_page); + memcpy(kaddr, ckpt, F2FS_BLKSIZE); + set_page_dirty(cp_page); + f2fs_put_page(cp_page, 1); + + for (i = 1; i < 1 + cp_payload_blks; i++) { + cp_page = grab_meta_page(sbi, start_blk++); + kaddr = page_address(cp_page); + memcpy(kaddr, (char *)ckpt + i * F2FS_BLKSIZE, F2FS_BLKSIZE); + set_page_dirty(cp_page); + f2fs_put_page(cp_page, 1); + } + + if (orphan_num) { + write_orphan_inodes(sbi, start_blk); + start_blk += orphan_blocks; + } + + write_data_summaries(sbi, start_blk); + start_blk += data_sum_blocks; + if (__remain_node_summaries(cpc->reason)) { + write_node_summaries(sbi, start_blk); + start_blk += NR_CURSEG_NODE_TYPE; + } + + /* writeout checkpoint block */ + cp_page = grab_meta_page(sbi, start_blk); + kaddr = page_address(cp_page); + memcpy(kaddr, ckpt, F2FS_BLKSIZE); + set_page_dirty(cp_page); + f2fs_put_page(cp_page, 1); + + /* wait for previous submitted node/meta pages writeback */ + wait_on_all_pages_writeback(sbi); + + if (unlikely(f2fs_cp_error(sbi))) + return; + + filemap_fdatawait_range(NODE_MAPPING(sbi), 0, LONG_MAX); + filemap_fdatawait_range(META_MAPPING(sbi), 0, LONG_MAX); + + /* update user_block_counts */ + sbi->last_valid_block_count = sbi->total_valid_block_count; + sbi->alloc_valid_block_count = 0; + + /* Here, we only have one bio having CP pack */ + sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + + /* wait for previous submitted meta pages writeback */ + wait_on_all_pages_writeback(sbi); + + release_dirty_inode(sbi); + + if (unlikely(f2fs_cp_error(sbi))) + return; + + clear_prefree_segments(sbi); + clear_sbi_flag(sbi, SBI_IS_DIRTY); +} + +/* + * We guarantee that this checkpoint procedure will not fail. + */ +void write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + unsigned long long ckpt_ver; + + mutex_lock(&sbi->cp_mutex); + + if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) && + (cpc->reason == CP_FASTBOOT || cpc->reason == CP_SYNC)) + goto out; + if (unlikely(f2fs_cp_error(sbi))) + goto out; + if (f2fs_readonly(sbi->sb)) + goto out; + + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "start block_ops"); + + if (block_operations(sbi)) + goto out; + + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish block_ops"); + + f2fs_submit_merged_bio(sbi, DATA, WRITE); + f2fs_submit_merged_bio(sbi, NODE, WRITE); + f2fs_submit_merged_bio(sbi, META, WRITE); + + /* + * update checkpoint pack index + * Increase the version number so that + * SIT entries and seg summaries are written at correct place + */ + ckpt_ver = cur_cp_version(ckpt); + ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver); + + /* write cached NAT/SIT entries to NAT/SIT area */ + flush_nat_entries(sbi); + flush_sit_entries(sbi, cpc); + + /* unlock all the fs_lock[] in do_checkpoint() */ + do_checkpoint(sbi, cpc); + + unblock_operations(sbi); + stat_inc_cp_count(sbi->stat_info); + + if (cpc->reason == CP_RECOVERY) + f2fs_msg(sbi->sb, KERN_NOTICE, + "checkpoint: version = %llx", ckpt_ver); +out: + mutex_unlock(&sbi->cp_mutex); + trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, "finish checkpoint"); +} + +void init_ino_entry_info(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = 0; i < MAX_INO_ENTRY; i++) { + struct inode_management *im = &sbi->im[i]; + + INIT_RADIX_TREE(&im->ino_root, GFP_ATOMIC); + spin_lock_init(&im->ino_lock); + INIT_LIST_HEAD(&im->ino_list); + im->ino_num = 0; + } + + sbi->max_orphans = (sbi->blocks_per_seg - F2FS_CP_PACKS - + NR_CURSEG_TYPE - __cp_payload(sbi)) * + F2FS_ORPHANS_PER_BLOCK; +} + +int __init create_checkpoint_caches(void) +{ + ino_entry_slab = f2fs_kmem_cache_create("f2fs_ino_entry", + sizeof(struct ino_entry)); + if (!ino_entry_slab) + return -ENOMEM; + inode_entry_slab = f2fs_kmem_cache_create("f2fs_inode_entry", + sizeof(struct inode_entry)); + if (!inode_entry_slab) { + kmem_cache_destroy(ino_entry_slab); + return -ENOMEM; + } + return 0; +} + +void destroy_checkpoint_caches(void) +{ + kmem_cache_destroy(ino_entry_slab); + kmem_cache_destroy(inode_entry_slab); +} diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c new file mode 100644 index 000000000000..aa1078a7669c --- /dev/null +++ b/fs/f2fs/data.c @@ -0,0 +1,1859 @@ +/* + * fs/f2fs/data.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "trace.h" +#include + +static struct kmem_cache *extent_tree_slab; +static struct kmem_cache *extent_node_slab; + +static void f2fs_read_end_io(struct bio *bio, int err) +{ + struct bio_vec *bvec; + int i; + + __bio_for_each_segment(bvec, bio, i, 0) { + struct page *page = bvec->bv_page; + + if (!err) { + SetPageUptodate(page); + } else { + ClearPageUptodate(page); + SetPageError(page); + } + unlock_page(page); + } + bio_put(bio); +} + +static void f2fs_write_end_io(struct bio *bio, int err) +{ + struct f2fs_sb_info *sbi = bio->bi_private; + struct bio_vec *bvec; + int i; + + __bio_for_each_segment(bvec, bio, i, 0) { + struct page *page = bvec->bv_page; + + if (unlikely(err)) { + set_page_dirty(page); + set_bit(AS_EIO, &page->mapping->flags); + f2fs_stop_checkpoint(sbi); + } + end_page_writeback(page); + dec_page_count(sbi, F2FS_WRITEBACK); + } + + if (!get_pages(sbi, F2FS_WRITEBACK) && + !list_empty(&sbi->cp_wait.task_list)) + wake_up(&sbi->cp_wait); + + bio_put(bio); +} + +/* + * Low-level block read/write IO operations. + */ +static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, + int npages, bool is_read) +{ + struct bio *bio; + + /* No failure on bio allocation */ + bio = bio_alloc(GFP_NOIO, npages); + + bio->bi_bdev = sbi->sb->s_bdev; + bio->bi_sector = SECTOR_FROM_BLOCK(blk_addr); + bio->bi_end_io = is_read ? f2fs_read_end_io : f2fs_write_end_io; + bio->bi_private = sbi; + + return bio; +} + +static void __submit_merged_bio(struct f2fs_bio_info *io) +{ + struct f2fs_io_info *fio = &io->fio; + + if (!io->bio) + return; + + if (is_read_io(fio->rw)) + trace_f2fs_submit_read_bio(io->sbi->sb, fio, io->bio); + else + trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); + + submit_bio(fio->rw, io->bio); + io->bio = NULL; +} + +void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, + enum page_type type, int rw) +{ + enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct f2fs_bio_info *io; + + io = is_read_io(rw) ? &sbi->read_io : &sbi->write_io[btype]; + + down_write(&io->io_rwsem); + + /* change META to META_FLUSH in the checkpoint procedure */ + if (type >= META_FLUSH) { + io->fio.type = META_FLUSH; + if (test_opt(sbi, NOBARRIER)) + io->fio.rw = WRITE_FLUSH | REQ_META | REQ_PRIO; + else + io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO; + } + __submit_merged_bio(io); + up_write(&io->io_rwsem); +} + +/* + * Fill the locked page with data located in the block address. + * Return unlocked page. + */ +int f2fs_submit_page_bio(struct f2fs_sb_info *sbi, struct page *page, + struct f2fs_io_info *fio) +{ + struct bio *bio; + + trace_f2fs_submit_page_bio(page, fio); + f2fs_trace_ios(page, fio, 0); + + /* Allocate a new bio */ + bio = __bio_alloc(sbi, fio->blk_addr, 1, is_read_io(fio->rw)); + + if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { + bio_put(bio); + f2fs_put_page(page, 1); + return -EFAULT; + } + + submit_bio(fio->rw, bio); + return 0; +} + +void f2fs_submit_page_mbio(struct f2fs_sb_info *sbi, struct page *page, + struct f2fs_io_info *fio) +{ + enum page_type btype = PAGE_TYPE_OF_BIO(fio->type); + struct f2fs_bio_info *io; + bool is_read = is_read_io(fio->rw); + + io = is_read ? &sbi->read_io : &sbi->write_io[btype]; + + verify_block_addr(sbi, fio->blk_addr); + + down_write(&io->io_rwsem); + + if (!is_read) + inc_page_count(sbi, F2FS_WRITEBACK); + + if (io->bio && (io->last_block_in_bio != fio->blk_addr - 1 || + io->fio.rw != fio->rw)) + __submit_merged_bio(io); +alloc_new: + if (io->bio == NULL) { + int bio_blocks = MAX_BIO_BLOCKS(sbi); + + io->bio = __bio_alloc(sbi, fio->blk_addr, bio_blocks, is_read); + io->fio = *fio; + } + + if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) < + PAGE_CACHE_SIZE) { + __submit_merged_bio(io); + goto alloc_new; + } + + io->last_block_in_bio = fio->blk_addr; + f2fs_trace_ios(page, fio, 0); + + up_write(&io->io_rwsem); + trace_f2fs_submit_page_mbio(page, fio); +} + +/* + * Lock ordering for the change of data block address: + * ->data_page + * ->node_page + * update block addresses in the node page + */ +void set_data_blkaddr(struct dnode_of_data *dn) +{ + struct f2fs_node *rn; + __le32 *addr_array; + struct page *node_page = dn->node_page; + unsigned int ofs_in_node = dn->ofs_in_node; + + f2fs_wait_on_page_writeback(node_page, NODE); + + rn = F2FS_NODE(node_page); + + /* Get physical address of data block */ + addr_array = blkaddr_in_node(rn); + addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr); + set_page_dirty(node_page); +} + +int reserve_new_block(struct dnode_of_data *dn) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + + if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + return -EPERM; + if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1))) + return -ENOSPC; + + trace_f2fs_reserve_new_block(dn->inode, dn->nid, dn->ofs_in_node); + + dn->data_blkaddr = NEW_ADDR; + set_data_blkaddr(dn); + mark_inode_dirty(dn->inode); + sync_inode_page(dn); + return 0; +} + +int f2fs_reserve_block(struct dnode_of_data *dn, pgoff_t index) +{ + bool need_put = dn->inode_page ? false : true; + int err; + + err = get_dnode_of_data(dn, index, ALLOC_NODE); + if (err) + return err; + + if (dn->data_blkaddr == NULL_ADDR) + err = reserve_new_block(dn); + if (err || need_put) + f2fs_put_dnode(dn); + return err; +} + +static void f2fs_map_bh(struct super_block *sb, pgoff_t pgofs, + struct extent_info *ei, struct buffer_head *bh_result) +{ + unsigned int blkbits = sb->s_blocksize_bits; + size_t max_size = bh_result->b_size; + size_t mapped_size; + + clear_buffer_new(bh_result); + map_bh(bh_result, sb, ei->blk + pgofs - ei->fofs); + mapped_size = (ei->fofs + ei->len - pgofs) << blkbits; + bh_result->b_size = min(max_size, mapped_size); +} + +static bool lookup_extent_info(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + pgoff_t start_fofs, end_fofs; + block_t start_blkaddr; + + read_lock(&fi->ext_lock); + if (fi->ext.len == 0) { + read_unlock(&fi->ext_lock); + return false; + } + + stat_inc_total_hit(inode->i_sb); + + start_fofs = fi->ext.fofs; + end_fofs = fi->ext.fofs + fi->ext.len - 1; + start_blkaddr = fi->ext.blk; + + if (pgofs >= start_fofs && pgofs <= end_fofs) { + *ei = fi->ext; + stat_inc_read_hit(inode->i_sb); + read_unlock(&fi->ext_lock); + return true; + } + read_unlock(&fi->ext_lock); + return false; +} + +static bool update_extent_info(struct inode *inode, pgoff_t fofs, + block_t blkaddr) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + pgoff_t start_fofs, end_fofs; + block_t start_blkaddr, end_blkaddr; + int need_update = true; + + write_lock(&fi->ext_lock); + + start_fofs = fi->ext.fofs; + end_fofs = fi->ext.fofs + fi->ext.len - 1; + start_blkaddr = fi->ext.blk; + end_blkaddr = fi->ext.blk + fi->ext.len - 1; + + /* Drop and initialize the matched extent */ + if (fi->ext.len == 1 && fofs == start_fofs) + fi->ext.len = 0; + + /* Initial extent */ + if (fi->ext.len == 0) { + if (blkaddr != NULL_ADDR) { + fi->ext.fofs = fofs; + fi->ext.blk = blkaddr; + fi->ext.len = 1; + } + goto end_update; + } + + /* Front merge */ + if (fofs == start_fofs - 1 && blkaddr == start_blkaddr - 1) { + fi->ext.fofs--; + fi->ext.blk--; + fi->ext.len++; + goto end_update; + } + + /* Back merge */ + if (fofs == end_fofs + 1 && blkaddr == end_blkaddr + 1) { + fi->ext.len++; + goto end_update; + } + + /* Split the existing extent */ + if (fi->ext.len > 1 && + fofs >= start_fofs && fofs <= end_fofs) { + if ((end_fofs - fofs) < (fi->ext.len >> 1)) { + fi->ext.len = fofs - start_fofs; + } else { + fi->ext.fofs = fofs + 1; + fi->ext.blk = start_blkaddr + fofs - start_fofs + 1; + fi->ext.len -= fofs - start_fofs + 1; + } + } else { + need_update = false; + } + + /* Finally, if the extent is very fragmented, let's drop the cache. */ + if (fi->ext.len < F2FS_MIN_EXTENT_LEN) { + fi->ext.len = 0; + set_inode_flag(fi, FI_NO_EXTENT); + need_update = true; + } +end_update: + write_unlock(&fi->ext_lock); + return need_update; +} + +static struct extent_node *__attach_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei, + struct rb_node *parent, struct rb_node **p) +{ + struct extent_node *en; + + en = kmem_cache_alloc(extent_node_slab, GFP_ATOMIC); + if (!en) + return NULL; + + en->ei = *ei; + INIT_LIST_HEAD(&en->list); + + rb_link_node(&en->rb_node, parent, p); + rb_insert_color(&en->rb_node, &et->root); + et->count++; + atomic_inc(&sbi->total_ext_node); + return en; +} + +static void __detach_extent_node(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_node *en) +{ + rb_erase(&en->rb_node, &et->root); + et->count--; + atomic_dec(&sbi->total_ext_node); + + if (et->cached_en == en) + et->cached_en = NULL; +} + +static struct extent_tree *__find_extent_tree(struct f2fs_sb_info *sbi, + nid_t ino) +{ + struct extent_tree *et; + + down_read(&sbi->extent_tree_lock); + et = radix_tree_lookup(&sbi->extent_tree_root, ino); + if (!et) { + up_read(&sbi->extent_tree_lock); + return NULL; + } + atomic_inc(&et->refcount); + up_read(&sbi->extent_tree_lock); + + return et; +} + +static struct extent_tree *__grab_extent_tree(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + nid_t ino = inode->i_ino; + + down_write(&sbi->extent_tree_lock); + et = radix_tree_lookup(&sbi->extent_tree_root, ino); + if (!et) { + et = f2fs_kmem_cache_alloc(extent_tree_slab, GFP_NOFS); + f2fs_radix_tree_insert(&sbi->extent_tree_root, ino, et); + memset(et, 0, sizeof(struct extent_tree)); + et->ino = ino; + et->root = RB_ROOT; + et->cached_en = NULL; + rwlock_init(&et->lock); + atomic_set(&et->refcount, 0); + et->count = 0; + sbi->total_ext_tree++; + } + atomic_inc(&et->refcount); + up_write(&sbi->extent_tree_lock); + + return et; +} + +static struct extent_node *__lookup_extent_tree(struct extent_tree *et, + unsigned int fofs) +{ + struct rb_node *node = et->root.rb_node; + struct extent_node *en; + + if (et->cached_en) { + struct extent_info *cei = &et->cached_en->ei; + + if (cei->fofs <= fofs && cei->fofs + cei->len > fofs) + return et->cached_en; + } + + while (node) { + en = rb_entry(node, struct extent_node, rb_node); + + if (fofs < en->ei.fofs) { + node = node->rb_left; + } else if (fofs >= en->ei.fofs + en->ei.len) { + node = node->rb_right; + } else { + et->cached_en = en; + return en; + } + } + return NULL; +} + +static struct extent_node *__try_back_merge(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_node *en) +{ + struct extent_node *prev; + struct rb_node *node; + + node = rb_prev(&en->rb_node); + if (!node) + return NULL; + + prev = rb_entry(node, struct extent_node, rb_node); + if (__is_back_mergeable(&en->ei, &prev->ei)) { + en->ei.fofs = prev->ei.fofs; + en->ei.blk = prev->ei.blk; + en->ei.len += prev->ei.len; + __detach_extent_node(sbi, et, prev); + return prev; + } + return NULL; +} + +static struct extent_node *__try_front_merge(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_node *en) +{ + struct extent_node *next; + struct rb_node *node; + + node = rb_next(&en->rb_node); + if (!node) + return NULL; + + next = rb_entry(node, struct extent_node, rb_node); + if (__is_front_mergeable(&en->ei, &next->ei)) { + en->ei.len += next->ei.len; + __detach_extent_node(sbi, et, next); + return next; + } + return NULL; +} + +static struct extent_node *__insert_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, struct extent_info *ei, + struct extent_node **den) +{ + struct rb_node **p = &et->root.rb_node; + struct rb_node *parent = NULL; + struct extent_node *en; + + while (*p) { + parent = *p; + en = rb_entry(parent, struct extent_node, rb_node); + + if (ei->fofs < en->ei.fofs) { + if (__is_front_mergeable(ei, &en->ei)) { + f2fs_bug_on(sbi, !den); + en->ei.fofs = ei->fofs; + en->ei.blk = ei->blk; + en->ei.len += ei->len; + *den = __try_back_merge(sbi, et, en); + return en; + } + p = &(*p)->rb_left; + } else if (ei->fofs >= en->ei.fofs + en->ei.len) { + if (__is_back_mergeable(ei, &en->ei)) { + f2fs_bug_on(sbi, !den); + en->ei.len += ei->len; + *den = __try_front_merge(sbi, et, en); + return en; + } + p = &(*p)->rb_right; + } else { + f2fs_bug_on(sbi, 1); + } + } + + return __attach_extent_node(sbi, et, ei, parent, p); +} + +static unsigned int __free_extent_tree(struct f2fs_sb_info *sbi, + struct extent_tree *et, bool free_all) +{ + struct rb_node *node, *next; + struct extent_node *en; + unsigned int count = et->count; + + node = rb_first(&et->root); + while (node) { + next = rb_next(node); + en = rb_entry(node, struct extent_node, rb_node); + + if (free_all) { + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_del_init(&en->list); + spin_unlock(&sbi->extent_lock); + } + + if (free_all || list_empty(&en->list)) { + __detach_extent_node(sbi, et, en); + kmem_cache_free(extent_node_slab, en); + } + node = next; + } + + return count - et->count; +} + +static void f2fs_init_extent_tree(struct inode *inode, + struct f2fs_extent *i_ext) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + struct extent_node *en; + struct extent_info ei; + + if (le32_to_cpu(i_ext->len) < F2FS_MIN_EXTENT_LEN) + return; + + et = __grab_extent_tree(inode); + + write_lock(&et->lock); + if (et->count) + goto out; + + set_extent_info(&ei, le32_to_cpu(i_ext->fofs), + le32_to_cpu(i_ext->blk), le32_to_cpu(i_ext->len)); + + en = __insert_extent_tree(sbi, et, &ei, NULL); + if (en) { + et->cached_en = en; + + spin_lock(&sbi->extent_lock); + list_add_tail(&en->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); + } +out: + write_unlock(&et->lock); + atomic_dec(&et->refcount); +} + +static bool f2fs_lookup_extent_tree(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + struct extent_node *en; + + trace_f2fs_lookup_extent_tree_start(inode, pgofs); + + et = __find_extent_tree(sbi, inode->i_ino); + if (!et) + return false; + + read_lock(&et->lock); + en = __lookup_extent_tree(et, pgofs); + if (en) { + *ei = en->ei; + spin_lock(&sbi->extent_lock); + if (!list_empty(&en->list)) + list_move_tail(&en->list, &sbi->extent_list); + spin_unlock(&sbi->extent_lock); + stat_inc_read_hit(sbi->sb); + } + stat_inc_total_hit(sbi->sb); + read_unlock(&et->lock); + + trace_f2fs_lookup_extent_tree_end(inode, pgofs, en); + + atomic_dec(&et->refcount); + return en ? true : false; +} + +static void f2fs_update_extent_tree(struct inode *inode, pgoff_t fofs, + block_t blkaddr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + struct extent_node *en = NULL, *en1 = NULL, *en2 = NULL, *en3 = NULL; + struct extent_node *den = NULL; + struct extent_info ei, dei; + unsigned int endofs; + + trace_f2fs_update_extent_tree(inode, fofs, blkaddr); + + et = __grab_extent_tree(inode); + + write_lock(&et->lock); + + /* 1. lookup and remove existing extent info in cache */ + en = __lookup_extent_tree(et, fofs); + if (!en) + goto update_extent; + + dei = en->ei; + __detach_extent_node(sbi, et, en); + + /* 2. if extent can be split more, split and insert the left part */ + if (dei.len > 1) { + /* insert left part of split extent into cache */ + if (fofs - dei.fofs >= F2FS_MIN_EXTENT_LEN) { + set_extent_info(&ei, dei.fofs, dei.blk, + fofs - dei.fofs); + en1 = __insert_extent_tree(sbi, et, &ei, NULL); + } + + /* insert right part of split extent into cache */ + endofs = dei.fofs + dei.len - 1; + if (endofs - fofs >= F2FS_MIN_EXTENT_LEN) { + set_extent_info(&ei, fofs + 1, + fofs - dei.fofs + dei.blk, endofs - fofs); + en2 = __insert_extent_tree(sbi, et, &ei, NULL); + } + } + +update_extent: + /* 3. update extent in extent cache */ + if (blkaddr) { + set_extent_info(&ei, fofs, blkaddr, 1); + en3 = __insert_extent_tree(sbi, et, &ei, &den); + } + + /* 4. update in global extent list */ + spin_lock(&sbi->extent_lock); + if (en && !list_empty(&en->list)) + list_del(&en->list); + /* + * en1 and en2 split from en, they will become more and more smaller + * fragments after splitting several times. So if the length is smaller + * than F2FS_MIN_EXTENT_LEN, we will not add them into extent tree. + */ + if (en1) + list_add_tail(&en1->list, &sbi->extent_list); + if (en2) + list_add_tail(&en2->list, &sbi->extent_list); + if (en3) { + if (list_empty(&en3->list)) + list_add_tail(&en3->list, &sbi->extent_list); + else + list_move_tail(&en3->list, &sbi->extent_list); + } + if (den && !list_empty(&den->list)) + list_del(&den->list); + spin_unlock(&sbi->extent_lock); + + /* 5. release extent node */ + if (en) + kmem_cache_free(extent_node_slab, en); + if (den) + kmem_cache_free(extent_node_slab, den); + + write_unlock(&et->lock); + atomic_dec(&et->refcount); +} + +void f2fs_preserve_extent_tree(struct inode *inode) +{ + struct extent_tree *et; + struct extent_info *ext = &F2FS_I(inode)->ext; + bool sync = false; + + if (!test_opt(F2FS_I_SB(inode), EXTENT_CACHE)) + return; + + et = __find_extent_tree(F2FS_I_SB(inode), inode->i_ino); + if (!et) { + if (ext->len) { + ext->len = 0; + update_inode_page(inode); + } + return; + } + + read_lock(&et->lock); + if (et->count) { + struct extent_node *en; + + if (et->cached_en) { + en = et->cached_en; + } else { + struct rb_node *node = rb_first(&et->root); + + if (!node) + node = rb_last(&et->root); + en = rb_entry(node, struct extent_node, rb_node); + } + + if (__is_extent_same(ext, &en->ei)) + goto out; + + *ext = en->ei; + sync = true; + } else if (ext->len) { + ext->len = 0; + sync = true; + } +out: + read_unlock(&et->lock); + atomic_dec(&et->refcount); + + if (sync) + update_inode_page(inode); +} + +void f2fs_shrink_extent_tree(struct f2fs_sb_info *sbi, int nr_shrink) +{ + struct extent_tree *treevec[EXT_TREE_VEC_SIZE]; + struct extent_node *en, *tmp; + unsigned long ino = F2FS_ROOT_INO(sbi); + struct radix_tree_iter iter; + void **slot; + unsigned int found; + unsigned int node_cnt = 0, tree_cnt = 0; + + if (!test_opt(sbi, EXTENT_CACHE)) + return; + + if (available_free_memory(sbi, EXTENT_CACHE)) + return; + + spin_lock(&sbi->extent_lock); + list_for_each_entry_safe(en, tmp, &sbi->extent_list, list) { + if (!nr_shrink--) + break; + list_del_init(&en->list); + } + spin_unlock(&sbi->extent_lock); + + down_read(&sbi->extent_tree_lock); + while ((found = radix_tree_gang_lookup(&sbi->extent_tree_root, + (void **)treevec, ino, EXT_TREE_VEC_SIZE))) { + unsigned i; + + ino = treevec[found - 1]->ino + 1; + for (i = 0; i < found; i++) { + struct extent_tree *et = treevec[i]; + + atomic_inc(&et->refcount); + write_lock(&et->lock); + node_cnt += __free_extent_tree(sbi, et, false); + write_unlock(&et->lock); + atomic_dec(&et->refcount); + } + } + up_read(&sbi->extent_tree_lock); + + down_write(&sbi->extent_tree_lock); + radix_tree_for_each_slot(slot, &sbi->extent_tree_root, &iter, + F2FS_ROOT_INO(sbi)) { + struct extent_tree *et = (struct extent_tree *)*slot; + + if (!atomic_read(&et->refcount) && !et->count) { + radix_tree_delete(&sbi->extent_tree_root, et->ino); + kmem_cache_free(extent_tree_slab, et); + sbi->total_ext_tree--; + tree_cnt++; + } + } + up_write(&sbi->extent_tree_lock); + + trace_f2fs_shrink_extent_tree(sbi, node_cnt, tree_cnt); +} + +void f2fs_destroy_extent_tree(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct extent_tree *et; + unsigned int node_cnt = 0; + + if (!test_opt(sbi, EXTENT_CACHE)) + return; + + et = __find_extent_tree(sbi, inode->i_ino); + if (!et) + goto out; + + /* free all extent info belong to this extent tree */ + write_lock(&et->lock); + node_cnt = __free_extent_tree(sbi, et, true); + write_unlock(&et->lock); + + atomic_dec(&et->refcount); + + /* try to find and delete extent tree entry in radix tree */ + down_write(&sbi->extent_tree_lock); + et = radix_tree_lookup(&sbi->extent_tree_root, inode->i_ino); + if (!et) { + up_write(&sbi->extent_tree_lock); + goto out; + } + f2fs_bug_on(sbi, atomic_read(&et->refcount) || et->count); + radix_tree_delete(&sbi->extent_tree_root, inode->i_ino); + kmem_cache_free(extent_tree_slab, et); + sbi->total_ext_tree--; + up_write(&sbi->extent_tree_lock); +out: + trace_f2fs_destroy_extent_tree(inode, node_cnt); + return; +} + +void f2fs_init_extent_cache(struct inode *inode, struct f2fs_extent *i_ext) +{ + if (test_opt(F2FS_I_SB(inode), EXTENT_CACHE)) + f2fs_init_extent_tree(inode, i_ext); + + write_lock(&F2FS_I(inode)->ext_lock); + get_extent_info(&F2FS_I(inode)->ext, *i_ext); + write_unlock(&F2FS_I(inode)->ext_lock); +} + +static bool f2fs_lookup_extent_cache(struct inode *inode, pgoff_t pgofs, + struct extent_info *ei) +{ + if (is_inode_flag_set(F2FS_I(inode), FI_NO_EXTENT)) + return false; + + if (test_opt(F2FS_I_SB(inode), EXTENT_CACHE)) + return f2fs_lookup_extent_tree(inode, pgofs, ei); + + return lookup_extent_info(inode, pgofs, ei); +} + +void f2fs_update_extent_cache(struct dnode_of_data *dn) +{ + struct f2fs_inode_info *fi = F2FS_I(dn->inode); + pgoff_t fofs; + + f2fs_bug_on(F2FS_I_SB(dn->inode), dn->data_blkaddr == NEW_ADDR); + + if (is_inode_flag_set(fi, FI_NO_EXTENT)) + return; + + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + + dn->ofs_in_node; + + if (test_opt(F2FS_I_SB(dn->inode), EXTENT_CACHE)) + return f2fs_update_extent_tree(dn->inode, fofs, + dn->data_blkaddr); + + if (update_extent_info(dn->inode, fofs, dn->data_blkaddr)) + sync_inode_page(dn); +} + +struct page *find_data_page(struct inode *inode, pgoff_t index, bool sync) +{ + struct address_space *mapping = inode->i_mapping; + struct dnode_of_data dn; + struct page *page; + struct extent_info ei; + int err; + struct f2fs_io_info fio = { + .type = DATA, + .rw = sync ? READ_SYNC : READA, + }; + + /* + * If sync is false, it needs to check its block allocation. + * This is need and triggered by two flows: + * gc and truncate_partial_data_page. + */ + if (!sync) + goto search; + + page = find_get_page(mapping, index); + if (page && PageUptodate(page)) + return page; + f2fs_put_page(page, 0); +search: + if (f2fs_lookup_extent_cache(inode, index, &ei)) { + dn.data_blkaddr = ei.blk + index - ei.fofs; + goto got_it; + } + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err) + return ERR_PTR(err); + f2fs_put_dnode(&dn); + + if (dn.data_blkaddr == NULL_ADDR) + return ERR_PTR(-ENOENT); + + /* By fallocate(), there is no cached page, but with NEW_ADDR */ + if (unlikely(dn.data_blkaddr == NEW_ADDR)) + return ERR_PTR(-EINVAL); + +got_it: + page = grab_cache_page(mapping, index); + if (!page) + return ERR_PTR(-ENOMEM); + + if (PageUptodate(page)) { + unlock_page(page); + return page; + } + + fio.blk_addr = dn.data_blkaddr; + err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); + if (err) + return ERR_PTR(err); + + if (sync) { + wait_on_page_locked(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 0); + return ERR_PTR(-EIO); + } + } + return page; +} + +/* + * If it tries to access a hole, return an error. + * Because, the callers, functions in dir.c and GC, should be able to know + * whether this page exists or not. + */ +struct page *get_lock_data_page(struct inode *inode, pgoff_t index) +{ + struct address_space *mapping = inode->i_mapping; + struct dnode_of_data dn; + struct page *page; + struct extent_info ei; + int err; + struct f2fs_io_info fio = { + .type = DATA, + .rw = READ_SYNC, + }; +repeat: + page = grab_cache_page(mapping, index); + if (!page) + return ERR_PTR(-ENOMEM); + + if (f2fs_lookup_extent_cache(inode, index, &ei)) { + dn.data_blkaddr = ei.blk + index - ei.fofs; + goto got_it; + } + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err) { + f2fs_put_page(page, 1); + return ERR_PTR(err); + } + f2fs_put_dnode(&dn); + + if (unlikely(dn.data_blkaddr == NULL_ADDR)) { + f2fs_put_page(page, 1); + return ERR_PTR(-ENOENT); + } + +got_it: + if (PageUptodate(page)) + return page; + + /* + * A new dentry page is allocated but not able to be written, since its + * new inode page couldn't be allocated due to -ENOSPC. + * In such the case, its blkaddr can be remained as NEW_ADDR. + * see, f2fs_add_link -> get_new_data_page -> init_inode_metadata. + */ + if (dn.data_blkaddr == NEW_ADDR) { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + return page; + } + + fio.blk_addr = dn.data_blkaddr; + err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); + if (err) + return ERR_PTR(err); + + lock_page(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + return ERR_PTR(-EIO); + } + if (unlikely(page->mapping != mapping)) { + f2fs_put_page(page, 1); + goto repeat; + } + return page; +} + +/* + * Caller ensures that this data page is never allocated. + * A new zero-filled data page is allocated in the page cache. + * + * Also, caller should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op(). + * Note that, ipage is set only by make_empty_dir. + */ +struct page *get_new_data_page(struct inode *inode, + struct page *ipage, pgoff_t index, bool new_i_size) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page; + struct dnode_of_data dn; + int err; + + set_new_dnode(&dn, inode, ipage, NULL, 0); + err = f2fs_reserve_block(&dn, index); + if (err) + return ERR_PTR(err); +repeat: + page = grab_cache_page(mapping, index); + if (!page) { + err = -ENOMEM; + goto put_err; + } + + if (PageUptodate(page)) + return page; + + if (dn.data_blkaddr == NEW_ADDR) { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + SetPageUptodate(page); + } else { + struct f2fs_io_info fio = { + .type = DATA, + .rw = READ_SYNC, + .blk_addr = dn.data_blkaddr, + }; + err = f2fs_submit_page_bio(F2FS_I_SB(inode), page, &fio); + if (err) + goto put_err; + + lock_page(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + err = -EIO; + goto put_err; + } + if (unlikely(page->mapping != mapping)) { + f2fs_put_page(page, 1); + goto repeat; + } + } + + if (new_i_size && + i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { + i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); + /* Only the directory inode sets new_i_size */ + set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + } + return page; + +put_err: + f2fs_put_dnode(&dn); + return ERR_PTR(err); +} + +static int __allocate_data_block(struct dnode_of_data *dn) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_inode_info *fi = F2FS_I(dn->inode); + struct f2fs_summary sum; + struct node_info ni; + int seg = CURSEG_WARM_DATA; + pgoff_t fofs; + + if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + return -EPERM; + + dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + if (dn->data_blkaddr == NEW_ADDR) + goto alloc; + + if (unlikely(!inc_valid_block_count(sbi, dn->inode, 1))) + return -ENOSPC; + +alloc: + get_node_info(sbi, dn->nid, &ni); + set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); + + if (dn->ofs_in_node == 0 && dn->inode_page == dn->node_page) + seg = CURSEG_DIRECT_IO; + + allocate_data_block(sbi, NULL, dn->data_blkaddr, &dn->data_blkaddr, + &sum, seg); + + /* direct IO doesn't use extent cache to maximize the performance */ + set_data_blkaddr(dn); + + /* update i_size */ + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + + dn->ofs_in_node; + if (i_size_read(dn->inode) < ((fofs + 1) << PAGE_CACHE_SHIFT)) + i_size_write(dn->inode, ((fofs + 1) << PAGE_CACHE_SHIFT)); + + return 0; +} + +static void __allocate_data_blocks(struct inode *inode, loff_t offset, + size_t count) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + u64 start = F2FS_BYTES_TO_BLK(offset); + u64 len = F2FS_BYTES_TO_BLK(count); + bool allocated; + u64 end_offset; + + while (len) { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); + + /* When reading holes, we need its node page */ + set_new_dnode(&dn, inode, NULL, NULL, 0); + if (get_dnode_of_data(&dn, start, ALLOC_NODE)) + goto out; + + allocated = false; + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + + while (dn.ofs_in_node < end_offset && len) { + block_t blkaddr; + + blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + if (blkaddr == NULL_ADDR || blkaddr == NEW_ADDR) { + if (__allocate_data_block(&dn)) + goto sync_out; + allocated = true; + } + len--; + start++; + dn.ofs_in_node++; + } + + if (allocated) + sync_inode_page(&dn); + + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + } + return; + +sync_out: + if (allocated) + sync_inode_page(&dn); + f2fs_put_dnode(&dn); +out: + f2fs_unlock_op(sbi); + return; +} + +/* + * get_data_block() now supported readahead/bmap/rw direct_IO with mapped bh. + * If original data blocks are allocated, then give them to blockdev. + * Otherwise, + * a. preallocate requested block addresses + * b. do not use extent cache for better performance + * c. give the block addresses to blockdev + */ +static int __get_data_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create, bool fiemap) +{ + unsigned int blkbits = inode->i_sb->s_blocksize_bits; + unsigned maxblocks = bh_result->b_size >> blkbits; + struct dnode_of_data dn; + int mode = create ? ALLOC_NODE : LOOKUP_NODE_RA; + pgoff_t pgofs, end_offset; + int err = 0, ofs = 1; + struct extent_info ei; + bool allocated = false; + + /* Get the page offset from the block offset(iblock) */ + pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits)); + + if (f2fs_lookup_extent_cache(inode, pgofs, &ei)) { + f2fs_map_bh(inode->i_sb, pgofs, &ei, bh_result); + goto out; + } + + if (create) + f2fs_lock_op(F2FS_I_SB(inode)); + + /* When reading holes, we need its node page */ + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, pgofs, mode); + if (err) { + if (err == -ENOENT) + err = 0; + goto unlock_out; + } + if (dn.data_blkaddr == NEW_ADDR && !fiemap) + goto put_out; + + if (dn.data_blkaddr != NULL_ADDR) { + clear_buffer_new(bh_result); + map_bh(bh_result, inode->i_sb, dn.data_blkaddr); + } else if (create) { + err = __allocate_data_block(&dn); + if (err) + goto put_out; + allocated = true; + set_buffer_new(bh_result); + map_bh(bh_result, inode->i_sb, dn.data_blkaddr); + } else { + goto put_out; + } + + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + bh_result->b_size = (((size_t)1) << blkbits); + dn.ofs_in_node++; + pgofs++; + +get_next: + if (dn.ofs_in_node >= end_offset) { + if (allocated) + sync_inode_page(&dn); + allocated = false; + f2fs_put_dnode(&dn); + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, pgofs, mode); + if (err) { + if (err == -ENOENT) + err = 0; + goto unlock_out; + } + if (dn.data_blkaddr == NEW_ADDR && !fiemap) + goto put_out; + + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + } + + if (maxblocks > (bh_result->b_size >> blkbits)) { + block_t blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + if (blkaddr == NULL_ADDR && create) { + err = __allocate_data_block(&dn); + if (err) + goto sync_out; + allocated = true; + set_buffer_new(bh_result); + blkaddr = dn.data_blkaddr; + } + /* Give more consecutive addresses for the readahead */ + if (blkaddr == (bh_result->b_blocknr + ofs)) { + ofs++; + dn.ofs_in_node++; + pgofs++; + bh_result->b_size += (((size_t)1) << blkbits); + goto get_next; + } + } +sync_out: + if (allocated) + sync_inode_page(&dn); +put_out: + f2fs_put_dnode(&dn); +unlock_out: + if (create) + f2fs_unlock_op(F2FS_I_SB(inode)); +out: + trace_f2fs_get_data_block(inode, iblock, bh_result, err); + return err; +} + +static int get_data_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + return __get_data_block(inode, iblock, bh_result, create, false); +} + +static int get_data_block_fiemap(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + return __get_data_block(inode, iblock, bh_result, create, true); +} + +int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + u64 start, u64 len) +{ + return generic_block_fiemap(inode, fieinfo, + start, len, get_data_block_fiemap); +} + +static int f2fs_read_data_page(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + int ret = -EAGAIN; + + trace_f2fs_readpage(page, DATA); + + /* If the file has inline data, try to read it directly */ + if (f2fs_has_inline_data(inode)) + ret = f2fs_read_inline_data(inode, page); + if (ret == -EAGAIN) + ret = mpage_readpage(page, get_data_block); + + return ret; +} + +static int f2fs_read_data_pages(struct file *file, + struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + struct inode *inode = file->f_mapping->host; + + /* If the file has inline data, skip readpages */ + if (f2fs_has_inline_data(inode)) + return 0; + + return mpage_readpages(mapping, pages, nr_pages, get_data_block); +} + +int do_write_data_page(struct page *page, struct f2fs_io_info *fio) +{ + struct inode *inode = page->mapping->host; + struct dnode_of_data dn; + int err = 0; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE); + if (err) + return err; + + fio->blk_addr = dn.data_blkaddr; + + /* This page is already truncated */ + if (fio->blk_addr == NULL_ADDR) { + ClearPageUptodate(page); + goto out_writepage; + } + + set_page_writeback(page); + + /* + * If current allocation needs SSR, + * it had better in-place writes for updated data. + */ + if (unlikely(fio->blk_addr != NEW_ADDR && + !is_cold_data(page) && + need_inplace_update(inode))) { + rewrite_data_page(page, fio); + set_inode_flag(F2FS_I(inode), FI_UPDATE_WRITE); + trace_f2fs_do_write_data_page(page, IPU); + } else { + write_data_page(page, &dn, fio); + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + trace_f2fs_do_write_data_page(page, OPU); + set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + if (page->index == 0) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + } +out_writepage: + f2fs_put_dnode(&dn); + return err; +} + +static int f2fs_write_data_page(struct page *page, + struct writeback_control *wbc) +{ + struct inode *inode = page->mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + loff_t i_size = i_size_read(inode); + const pgoff_t end_index = ((unsigned long long) i_size) + >> PAGE_CACHE_SHIFT; + unsigned offset = 0; + bool need_balance_fs = false; + int err = 0; + struct f2fs_io_info fio = { + .type = DATA, + .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + }; + + trace_f2fs_writepage(page, DATA); + + if (page->index < end_index) + goto write; + + /* + * If the offset is out-of-range of file size, + * this page does not have to be written to disk. + */ + offset = i_size & (PAGE_CACHE_SIZE - 1); + if ((page->index >= end_index + 1) || !offset) + goto out; + + zero_user_segment(page, offset, PAGE_CACHE_SIZE); +write: + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (f2fs_is_drop_cache(inode)) + goto out; + if (f2fs_is_volatile_file(inode) && !wbc->for_reclaim && + available_free_memory(sbi, BASE_CHECK)) + goto redirty_out; + + /* Dentry blocks are controlled by checkpoint */ + if (S_ISDIR(inode->i_mode)) { + if (unlikely(f2fs_cp_error(sbi))) + goto redirty_out; + err = do_write_data_page(page, &fio); + goto done; + } + + /* we should bypass data pages to proceed the kworkder jobs */ + if (unlikely(f2fs_cp_error(sbi))) { + SetPageError(page); + goto out; + } + + if (!wbc->for_reclaim) + need_balance_fs = true; + else if (has_not_enough_free_secs(sbi, 0)) + goto redirty_out; + + err = -EAGAIN; + f2fs_lock_op(sbi); + if (f2fs_has_inline_data(inode)) + err = f2fs_write_inline_data(inode, page); + if (err == -EAGAIN) + err = do_write_data_page(page, &fio); + f2fs_unlock_op(sbi); +done: + if (err && err != -ENOENT) + goto redirty_out; + + clear_cold_data(page); +out: + inode_dec_dirty_pages(inode); + if (err) + ClearPageUptodate(page); + unlock_page(page); + if (need_balance_fs) + f2fs_balance_fs(sbi); + if (wbc->for_reclaim) + f2fs_submit_merged_bio(sbi, DATA, WRITE); + return 0; + +redirty_out: + redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; +} + +static int __f2fs_writepage(struct page *page, struct writeback_control *wbc, + void *data) +{ + struct address_space *mapping = data; + int ret = mapping->a_ops->writepage(page, wbc); + mapping_set_error(mapping, ret); + return ret; +} + +static int f2fs_write_data_pages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct inode *inode = mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int ret; + long diff; + + trace_f2fs_writepages(mapping->host, wbc, DATA); + + /* deal with chardevs and other special file */ + if (!mapping->a_ops->writepage) + return 0; + + if (S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_NONE && + get_dirty_pages(inode) < nr_pages_to_skip(sbi, DATA) && + available_free_memory(sbi, DIRTY_DENTS)) + goto skip_write; + + /* during POR, we don't need to trigger writepage at all. */ + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto skip_write; + + diff = nr_pages_to_write(sbi, DATA, wbc); + + ret = write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); + + f2fs_submit_merged_bio(sbi, DATA, WRITE); + + remove_dirty_dir_inode(inode); + + wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); + return ret; + +skip_write: + wbc->pages_skipped += get_dirty_pages(inode); + return 0; +} + +static void f2fs_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > inode->i_size) { + truncate_pagecache(inode, 0, inode->i_size); + truncate_blocks(inode, inode->i_size, true); + } +} + +static int f2fs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + struct inode *inode = mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct page *page, *ipage; + pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; + struct dnode_of_data dn; + int err = 0; + + trace_f2fs_write_begin(inode, pos, len, flags); + + f2fs_balance_fs(sbi); + + /* + * We should check this at this moment to avoid deadlock on inode page + * and #0 page. The locking rule for inline_data conversion should be: + * lock_page(page #0) -> lock_page(inode_page) + */ + if (index != 0) { + err = f2fs_convert_inline_inode(inode); + if (err) + goto fail; + } +repeat: + page = grab_cache_page_write_begin(mapping, index, flags); + if (!page) { + err = -ENOMEM; + goto fail; + } + + *pagep = page; + + f2fs_lock_op(sbi); + + /* check inline_data */ + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto unlock_fail; + } + + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) { + if (pos + len <= MAX_INLINE_DATA) { + read_inline_data(page, ipage); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + sync_inode_page(&dn); + goto put_next; + } + err = f2fs_convert_inline_page(&dn, page); + if (err) + goto put_fail; + } + err = f2fs_reserve_block(&dn, index); + if (err) + goto put_fail; +put_next: + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + + if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) + return 0; + + f2fs_wait_on_page_writeback(page, DATA); + + if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { + unsigned start = pos & (PAGE_CACHE_SIZE - 1); + unsigned end = start + len; + + /* Reading beyond i_size is simple: memset to zero */ + zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); + goto out; + } + + if (dn.data_blkaddr == NEW_ADDR) { + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + } else { + struct f2fs_io_info fio = { + .type = DATA, + .rw = READ_SYNC, + .blk_addr = dn.data_blkaddr, + }; + err = f2fs_submit_page_bio(sbi, page, &fio); + if (err) + goto fail; + + lock_page(page); + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + err = -EIO; + goto fail; + } + if (unlikely(page->mapping != mapping)) { + f2fs_put_page(page, 1); + goto repeat; + } + } +out: + SetPageUptodate(page); + clear_cold_data(page); + return 0; + +put_fail: + f2fs_put_dnode(&dn); +unlock_fail: + f2fs_unlock_op(sbi); + f2fs_put_page(page, 1); +fail: + f2fs_write_failed(mapping, pos + len); + return err; +} + +static int f2fs_write_end(struct file *file, + struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ + struct inode *inode = page->mapping->host; + + trace_f2fs_write_end(inode, pos, len, copied); + + set_page_dirty(page); + + if (pos + copied > i_size_read(inode)) { + i_size_write(inode, pos + copied); + mark_inode_dirty(inode); + update_inode_page(inode); + } + + f2fs_put_page(page, 1); + return copied; +} + +static int check_direct_IO(struct inode *inode, int rw, + const struct iovec *iov, loff_t offset, unsigned long nr_segs) +{ + unsigned blocksize_mask = inode->i_sb->s_blocksize - 1; + int i; + + if (rw == READ) + return 0; + + if (offset & blocksize_mask) + return -EINVAL; + + for (i = 0; i < nr_segs; i++) + if (iov[i].iov_len & blocksize_mask) + return -EINVAL; + + return 0; +} + +static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, + unsigned long nr_segs) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + size_t count = iov_length(iov, nr_segs); + int err; + + /* we don't need to use inline_data strictly */ + if (f2fs_has_inline_data(inode)) { + err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + + if (check_direct_IO(inode, rw, iov, offset, nr_segs)) + return 0; + + trace_f2fs_direct_IO_enter(inode, offset, count, rw); + + if (rw & WRITE) + __allocate_data_blocks(inode, offset, count); + + err = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, + get_data_block); + if (err < 0 && (rw & WRITE)) + f2fs_write_failed(mapping, offset + count); + + trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); + + return err; +} + +void f2fs_invalidate_page(struct page *page, unsigned long offset) +{ + struct inode *inode = page->mapping->host; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (inode->i_ino >= F2FS_ROOT_INO(sbi) && (offset % PAGE_CACHE_SIZE)) + return; + + if (PageDirty(page)) { + if (inode->i_ino == F2FS_META_INO(sbi)) + dec_page_count(sbi, F2FS_DIRTY_META); + else if (inode->i_ino == F2FS_NODE_INO(sbi)) + dec_page_count(sbi, F2FS_DIRTY_NODES); + else + inode_dec_dirty_pages(inode); + } + ClearPagePrivate(page); +} + +int f2fs_release_page(struct page *page, gfp_t wait) +{ + /* If this is dirty page, keep PagePrivate */ + if (PageDirty(page)) + return 0; + + ClearPagePrivate(page); + return 1; +} + +static int f2fs_set_data_page_dirty(struct page *page) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + + trace_f2fs_set_page_dirty(page, DATA); + + SetPageUptodate(page); + + if (f2fs_is_atomic_file(inode)) { + register_inmem_page(inode, page); + return 1; + } + + mark_inode_dirty(inode); + + if (!PageDirty(page)) { + __set_page_dirty_nobuffers(page); + update_dirty_page(inode, page); + return 1; + } + return 0; +} + +static sector_t f2fs_bmap(struct address_space *mapping, sector_t block) +{ + struct inode *inode = mapping->host; + + /* we don't need to use inline_data strictly */ + if (f2fs_has_inline_data(inode)) { + int err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + return generic_block_bmap(mapping, block, get_data_block); +} + +void init_extent_cache_info(struct f2fs_sb_info *sbi) +{ + INIT_RADIX_TREE(&sbi->extent_tree_root, GFP_NOIO); + init_rwsem(&sbi->extent_tree_lock); + INIT_LIST_HEAD(&sbi->extent_list); + spin_lock_init(&sbi->extent_lock); + sbi->total_ext_tree = 0; + atomic_set(&sbi->total_ext_node, 0); +} + +int __init create_extent_cache(void) +{ + extent_tree_slab = f2fs_kmem_cache_create("f2fs_extent_tree", + sizeof(struct extent_tree)); + if (!extent_tree_slab) + return -ENOMEM; + extent_node_slab = f2fs_kmem_cache_create("f2fs_extent_node", + sizeof(struct extent_node)); + if (!extent_node_slab) { + kmem_cache_destroy(extent_tree_slab); + return -ENOMEM; + } + return 0; +} + +void destroy_extent_cache(void) +{ + kmem_cache_destroy(extent_node_slab); + kmem_cache_destroy(extent_tree_slab); +} + +const struct address_space_operations f2fs_dblock_aops = { + .readpage = f2fs_read_data_page, + .readpages = f2fs_read_data_pages, + .writepage = f2fs_write_data_page, + .writepages = f2fs_write_data_pages, + .write_begin = f2fs_write_begin, + .write_end = f2fs_write_end, + .set_page_dirty = f2fs_set_data_page_dirty, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, + .direct_IO = f2fs_direct_IO, + .bmap = f2fs_bmap, +}; diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c new file mode 100644 index 000000000000..f5388f37217e --- /dev/null +++ b/fs/f2fs/debug.c @@ -0,0 +1,413 @@ +/* + * f2fs debugging statistics + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Copyright (c) 2012 Linux Foundation + * Copyright (c) 2012 Greg Kroah-Hartman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "gc.h" + +static LIST_HEAD(f2fs_stat_list); +static struct dentry *f2fs_debugfs_root; +static DEFINE_MUTEX(f2fs_stat_mutex); + +static void update_general_status(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + int i; + + /* validation check of the segment numbers */ + si->hit_ext = sbi->read_hit_ext; + si->total_ext = sbi->total_hit_ext; + si->ext_tree = sbi->total_ext_tree; + si->ext_node = atomic_read(&sbi->total_ext_node); + si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); + si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); + si->ndirty_dirs = sbi->n_dirty_dirs; + si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); + si->inmem_pages = get_pages(sbi, F2FS_INMEM_PAGES); + si->wb_pages = get_pages(sbi, F2FS_WRITEBACK); + si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; + si->rsvd_segs = reserved_segments(sbi); + si->overp_segs = overprovision_segments(sbi); + si->valid_count = valid_user_blocks(sbi); + si->valid_node_count = valid_node_count(sbi); + si->valid_inode_count = valid_inode_count(sbi); + si->inline_inode = atomic_read(&sbi->inline_inode); + si->inline_dir = atomic_read(&sbi->inline_dir); + si->utilization = utilization(sbi); + + si->free_segs = free_segments(sbi); + si->free_secs = free_sections(sbi); + si->prefree_count = prefree_segments(sbi); + si->dirty_count = dirty_segments(sbi); + si->node_pages = NODE_MAPPING(sbi)->nrpages; + si->meta_pages = META_MAPPING(sbi)->nrpages; + si->nats = NM_I(sbi)->nat_cnt; + si->dirty_nats = NM_I(sbi)->dirty_nat_cnt; + si->sits = MAIN_SEGS(sbi); + si->dirty_sits = SIT_I(sbi)->dirty_sentries; + si->fnids = NM_I(sbi)->fcnt; + si->bg_gc = sbi->bg_gc; + si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) + * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) + / 2; + si->util_valid = (int)(written_block_count(sbi) >> + sbi->log_blocks_per_seg) + * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) + / 2; + si->util_invalid = 50 - si->util_free - si->util_valid; + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_NODE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + si->curseg[i] = curseg->segno; + si->cursec[i] = curseg->segno / sbi->segs_per_sec; + si->curzone[i] = si->cursec[i] / sbi->secs_per_zone; + } + + for (i = 0; i < 2; i++) { + si->segment_count[i] = sbi->segment_count[i]; + si->block_count[i] = sbi->block_count[i]; + } + + si->inplace_count = atomic_read(&sbi->inplace_count); +} + +/* + * This function calculates BDF of every segments + */ +static void update_sit_info(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist; + unsigned int segno, vblocks; + int ndirty = 0; + + bimodal = 0; + total_vblocks = 0; + blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg); + hblks_per_sec = blks_per_sec / 2; + for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { + vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); + dist = abs(vblocks - hblks_per_sec); + bimodal += dist * dist; + + if (vblocks > 0 && vblocks < blks_per_sec) { + total_vblocks += vblocks; + ndirty++; + } + } + dist = MAIN_SECS(sbi) * hblks_per_sec * hblks_per_sec / 100; + si->bimodal = bimodal / dist; + if (si->dirty_count) + si->avg_vblocks = total_vblocks / ndirty; + else + si->avg_vblocks = 0; +} + +/* + * This function calculates memory footprint. + */ +static void update_mem_info(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + unsigned npages; + int i; + + if (si->base_mem) + goto get_cache; + + si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; + si->base_mem += 2 * sizeof(struct f2fs_inode_info); + si->base_mem += sizeof(*sbi->ckpt); + + /* build sm */ + si->base_mem += sizeof(struct f2fs_sm_info); + + /* build sit */ + si->base_mem += sizeof(struct sit_info); + si->base_mem += MAIN_SEGS(sbi) * sizeof(struct seg_entry); + si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); + si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * MAIN_SEGS(sbi); + si->base_mem += SIT_VBLOCK_MAP_SIZE; + if (sbi->segs_per_sec > 1) + si->base_mem += MAIN_SECS(sbi) * sizeof(struct sec_entry); + si->base_mem += __bitmap_size(sbi, SIT_BITMAP); + + /* build free segmap */ + si->base_mem += sizeof(struct free_segmap_info); + si->base_mem += f2fs_bitmap_size(MAIN_SEGS(sbi)); + si->base_mem += f2fs_bitmap_size(MAIN_SECS(sbi)); + + /* build curseg */ + si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE; + si->base_mem += PAGE_CACHE_SIZE * NR_CURSEG_TYPE; + + /* build dirty segmap */ + si->base_mem += sizeof(struct dirty_seglist_info); + si->base_mem += NR_DIRTY_TYPE * f2fs_bitmap_size(MAIN_SEGS(sbi)); + si->base_mem += f2fs_bitmap_size(MAIN_SECS(sbi)); + + /* build nm */ + si->base_mem += sizeof(struct f2fs_nm_info); + si->base_mem += __bitmap_size(sbi, NAT_BITMAP); + +get_cache: + si->cache_mem = 0; + + /* build gc */ + if (sbi->gc_thread) + si->cache_mem += sizeof(struct f2fs_gc_kthread); + + /* build merge flush thread */ + if (SM_I(sbi)->cmd_control_info) + si->cache_mem += sizeof(struct flush_cmd_control); + + /* free nids */ + si->cache_mem += NM_I(sbi)->fcnt * sizeof(struct free_nid); + si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry); + si->cache_mem += NM_I(sbi)->dirty_nat_cnt * + sizeof(struct nat_entry_set); + si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages); + si->cache_mem += sbi->n_dirty_dirs * sizeof(struct inode_entry); + for (i = 0; i <= UPDATE_INO; i++) + si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry); + si->cache_mem += sbi->total_ext_tree * sizeof(struct extent_tree); + si->cache_mem += atomic_read(&sbi->total_ext_node) * + sizeof(struct extent_node); + + si->page_mem = 0; + npages = NODE_MAPPING(sbi)->nrpages; + si->page_mem += npages << PAGE_CACHE_SHIFT; + npages = META_MAPPING(sbi)->nrpages; + si->page_mem += npages << PAGE_CACHE_SHIFT; +} + +static int stat_show(struct seq_file *s, void *v) +{ + struct f2fs_stat_info *si; + int i = 0; + int j; + + mutex_lock(&f2fs_stat_mutex); + list_for_each_entry(si, &f2fs_stat_list, stat_list) { + char devname[BDEVNAME_SIZE]; + + update_general_status(si->sbi); + + seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n", + bdevname(si->sbi->sb->s_bdev, devname), i++); + seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", + si->sit_area_segs, si->nat_area_segs); + seq_printf(s, "[SSA: %d] [MAIN: %d", + si->ssa_area_segs, si->main_area_segs); + seq_printf(s, "(OverProv:%d Resv:%d)]\n\n", + si->overp_segs, si->rsvd_segs); + seq_printf(s, "Utilization: %d%% (%d valid blocks)\n", + si->utilization, si->valid_count); + seq_printf(s, " - Node: %u (Inode: %u, ", + si->valid_node_count, si->valid_inode_count); + seq_printf(s, "Other: %u)\n - Data: %u\n", + si->valid_node_count - si->valid_inode_count, + si->valid_count - si->valid_node_count); + seq_printf(s, " - Inline_data Inode: %u\n", + si->inline_inode); + seq_printf(s, " - Inline_dentry Inode: %u\n", + si->inline_dir); + seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", + si->main_area_segs, si->main_area_sections, + si->main_area_zones); + seq_printf(s, " - COLD data: %d, %d, %d\n", + si->curseg[CURSEG_COLD_DATA], + si->cursec[CURSEG_COLD_DATA], + si->curzone[CURSEG_COLD_DATA]); + seq_printf(s, " - WARM data: %d, %d, %d\n", + si->curseg[CURSEG_WARM_DATA], + si->cursec[CURSEG_WARM_DATA], + si->curzone[CURSEG_WARM_DATA]); + seq_printf(s, " - HOT data: %d, %d, %d\n", + si->curseg[CURSEG_HOT_DATA], + si->cursec[CURSEG_HOT_DATA], + si->curzone[CURSEG_HOT_DATA]); + seq_printf(s, " - Dir dnode: %d, %d, %d\n", + si->curseg[CURSEG_HOT_NODE], + si->cursec[CURSEG_HOT_NODE], + si->curzone[CURSEG_HOT_NODE]); + seq_printf(s, " - File dnode: %d, %d, %d\n", + si->curseg[CURSEG_WARM_NODE], + si->cursec[CURSEG_WARM_NODE], + si->curzone[CURSEG_WARM_NODE]); + seq_printf(s, " - Indir nodes: %d, %d, %d\n", + si->curseg[CURSEG_COLD_NODE], + si->cursec[CURSEG_COLD_NODE], + si->curzone[CURSEG_COLD_NODE]); + seq_printf(s, "\n - Valid: %d\n - Dirty: %d\n", + si->main_area_segs - si->dirty_count - + si->prefree_count - si->free_segs, + si->dirty_count); + seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", + si->prefree_count, si->free_segs, si->free_secs); + seq_printf(s, "CP calls: %d\n", si->cp_count); + seq_printf(s, "GC calls: %d (BG: %d)\n", + si->call_count, si->bg_gc); + seq_printf(s, " - data segments : %d (%d)\n", + si->data_segs, si->bg_data_segs); + seq_printf(s, " - node segments : %d (%d)\n", + si->node_segs, si->bg_node_segs); + seq_printf(s, "Try to move %d blocks (BG: %d)\n", si->tot_blks, + si->bg_data_blks + si->bg_node_blks); + seq_printf(s, " - data blocks : %d (%d)\n", si->data_blks, + si->bg_data_blks); + seq_printf(s, " - node blocks : %d (%d)\n", si->node_blks, + si->bg_node_blks); + seq_printf(s, "\nExtent Hit Ratio: %d / %d\n", + si->hit_ext, si->total_ext); + seq_printf(s, "\nExtent Tree Count: %d\n", si->ext_tree); + seq_printf(s, "\nExtent Node Count: %d\n", si->ext_node); + seq_puts(s, "\nBalancing F2FS Async:\n"); + seq_printf(s, " - inmem: %4d, wb: %4d\n", + si->inmem_pages, si->wb_pages); + seq_printf(s, " - nodes: %4d in %4d\n", + si->ndirty_node, si->node_pages); + seq_printf(s, " - dents: %4d in dirs:%4d\n", + si->ndirty_dent, si->ndirty_dirs); + seq_printf(s, " - meta: %4d in %4d\n", + si->ndirty_meta, si->meta_pages); + seq_printf(s, " - NATs: %9d/%9d\n - SITs: %9d/%9d\n", + si->dirty_nats, si->nats, si->dirty_sits, si->sits); + seq_printf(s, " - free_nids: %9d\n", + si->fnids); + seq_puts(s, "\nDistribution of User Blocks:"); + seq_puts(s, " [ valid | invalid | free ]\n"); + seq_puts(s, " ["); + + for (j = 0; j < si->util_valid; j++) + seq_putc(s, '-'); + seq_putc(s, '|'); + + for (j = 0; j < si->util_invalid; j++) + seq_putc(s, '-'); + seq_putc(s, '|'); + + for (j = 0; j < si->util_free; j++) + seq_putc(s, '-'); + seq_puts(s, "]\n\n"); + seq_printf(s, "IPU: %u blocks\n", si->inplace_count); + seq_printf(s, "SSR: %u blocks in %u segments\n", + si->block_count[SSR], si->segment_count[SSR]); + seq_printf(s, "LFS: %u blocks in %u segments\n", + si->block_count[LFS], si->segment_count[LFS]); + + /* segment usage info */ + update_sit_info(si->sbi); + seq_printf(s, "\nBDF: %u, avg. vblocks: %u\n", + si->bimodal, si->avg_vblocks); + + /* memory footprint */ + update_mem_info(si->sbi); + seq_printf(s, "\nMemory: %u KB\n", + (si->base_mem + si->cache_mem + si->page_mem) >> 10); + seq_printf(s, " - static: %u KB\n", + si->base_mem >> 10); + seq_printf(s, " - cached: %u KB\n", + si->cache_mem >> 10); + seq_printf(s, " - paged : %u KB\n", + si->page_mem >> 10); + } + mutex_unlock(&f2fs_stat_mutex); + return 0; +} + +static int stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, stat_show, inode->i_private); +} + +static const struct file_operations stat_fops = { + .open = stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int f2fs_build_stats(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_stat_info *si; + + si = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL); + if (!si) + return -ENOMEM; + + si->all_area_segs = le32_to_cpu(raw_super->segment_count); + si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit); + si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat); + si->ssa_area_segs = le32_to_cpu(raw_super->segment_count_ssa); + si->main_area_segs = le32_to_cpu(raw_super->segment_count_main); + si->main_area_sections = le32_to_cpu(raw_super->section_count); + si->main_area_zones = si->main_area_sections / + le32_to_cpu(raw_super->secs_per_zone); + si->sbi = sbi; + sbi->stat_info = si; + + atomic_set(&sbi->inline_inode, 0); + atomic_set(&sbi->inline_dir, 0); + atomic_set(&sbi->inplace_count, 0); + + mutex_lock(&f2fs_stat_mutex); + list_add_tail(&si->stat_list, &f2fs_stat_list); + mutex_unlock(&f2fs_stat_mutex); + + return 0; +} + +void f2fs_destroy_stats(struct f2fs_sb_info *sbi) +{ + struct f2fs_stat_info *si = F2FS_STAT(sbi); + + mutex_lock(&f2fs_stat_mutex); + list_del(&si->stat_list); + mutex_unlock(&f2fs_stat_mutex); + + kfree(si); +} + +void __init f2fs_create_root_stats(void) +{ + struct dentry *file; + + f2fs_debugfs_root = debugfs_create_dir("f2fs", NULL); + if (!f2fs_debugfs_root) + return; + + file = debugfs_create_file("status", S_IRUGO, f2fs_debugfs_root, + NULL, &stat_fops); + if (!file) { + debugfs_remove(f2fs_debugfs_root); + f2fs_debugfs_root = NULL; + } +} + +void f2fs_destroy_root_stats(void) +{ + if (!f2fs_debugfs_root) + return; + + debugfs_remove_recursive(f2fs_debugfs_root); + f2fs_debugfs_root = NULL; +} diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c new file mode 100644 index 000000000000..5575c90fe881 --- /dev/null +++ b/fs/f2fs/dir.c @@ -0,0 +1,820 @@ +/* + * fs/f2fs/dir.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "f2fs.h" +#include "node.h" +#include "acl.h" +#include "xattr.h" + +static unsigned long dir_blocks(struct inode *inode) +{ + return ((unsigned long long) (i_size_read(inode) + PAGE_CACHE_SIZE - 1)) + >> PAGE_CACHE_SHIFT; +} + +static unsigned int dir_buckets(unsigned int level, int dir_level) +{ + if (level + dir_level < MAX_DIR_HASH_DEPTH / 2) + return 1 << (level + dir_level); + else + return MAX_DIR_BUCKETS; +} + +static unsigned int bucket_blocks(unsigned int level) +{ + if (level < MAX_DIR_HASH_DEPTH / 2) + return 2; + else + return 4; +} + +unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { + [F2FS_FT_UNKNOWN] = DT_UNKNOWN, + [F2FS_FT_REG_FILE] = DT_REG, + [F2FS_FT_DIR] = DT_DIR, + [F2FS_FT_CHRDEV] = DT_CHR, + [F2FS_FT_BLKDEV] = DT_BLK, + [F2FS_FT_FIFO] = DT_FIFO, + [F2FS_FT_SOCK] = DT_SOCK, + [F2FS_FT_SYMLINK] = DT_LNK, +}; + +#define S_SHIFT 12 +static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { + [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, + [S_IFCHR >> S_SHIFT] = F2FS_FT_CHRDEV, + [S_IFBLK >> S_SHIFT] = F2FS_FT_BLKDEV, + [S_IFIFO >> S_SHIFT] = F2FS_FT_FIFO, + [S_IFSOCK >> S_SHIFT] = F2FS_FT_SOCK, + [S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK, +}; + +void set_de_type(struct f2fs_dir_entry *de, umode_t mode) +{ + de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; +} + +static unsigned long dir_block_index(unsigned int level, + int dir_level, unsigned int idx) +{ + unsigned long i; + unsigned long bidx = 0; + + for (i = 0; i < level; i++) + bidx += dir_buckets(i, dir_level) * bucket_blocks(i); + bidx += idx * bucket_blocks(level); + return bidx; +} + +static bool early_match_name(size_t namelen, f2fs_hash_t namehash, + struct f2fs_dir_entry *de) +{ + if (le16_to_cpu(de->name_len) != namelen) + return false; + + if (de->hash_code != namehash) + return false; + + return true; +} + +static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, + struct qstr *name, int *max_slots, + struct page **res_page) +{ + struct f2fs_dentry_block *dentry_blk; + struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; + + dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page); + + make_dentry_ptr(&d, (void *)dentry_blk, 1); + de = find_target_dentry(name, max_slots, &d); + + if (de) + *res_page = dentry_page; + else + kunmap(dentry_page); + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs has occurred. + */ + f2fs_bug_on(F2FS_P_SB(dentry_page), d.max < 0); + return de; +} + +struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots, + struct f2fs_dentry_ptr *d) +{ + struct f2fs_dir_entry *de; + unsigned long bit_pos = 0; + f2fs_hash_t namehash = f2fs_dentry_hash(name); + int max_len = 0; + + if (max_slots) + *max_slots = 0; + while (bit_pos < d->max) { + if (!test_bit_le(bit_pos, d->bitmap)) { + bit_pos++; + max_len++; + continue; + } + + de = &d->dentry[bit_pos]; + if (early_match_name(name->len, namehash, de) && + !memcmp(d->filename[bit_pos], name->name, name->len)) + goto found; + + if (max_slots && max_len > *max_slots) + *max_slots = max_len; + max_len = 0; + + /* remain bug on condition */ + if (unlikely(!de->name_len)) + d->max = -1; + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + + de = NULL; +found: + if (max_slots && max_len > *max_slots) + *max_slots = max_len; + return de; +} + +static struct f2fs_dir_entry *find_in_level(struct inode *dir, + unsigned int level, struct qstr *name, + f2fs_hash_t namehash, struct page **res_page) +{ + int s = GET_DENTRY_SLOTS(name->len); + unsigned int nbucket, nblock; + unsigned int bidx, end_block; + struct page *dentry_page; + struct f2fs_dir_entry *de = NULL; + bool room = false; + int max_slots; + + f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH); + + nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); + nblock = bucket_blocks(level); + + bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level, + le32_to_cpu(namehash) % nbucket); + end_block = bidx + nblock; + + for (; bidx < end_block; bidx++) { + /* no need to allocate new dentry pages to all the indices */ + dentry_page = find_data_page(dir, bidx, true); + if (IS_ERR(dentry_page)) { + room = true; + continue; + } + + de = find_in_block(dentry_page, name, &max_slots, res_page); + if (de) + break; + + if (max_slots >= s) + room = true; + f2fs_put_page(dentry_page, 0); + } + + if (!de && room && F2FS_I(dir)->chash != namehash) { + F2FS_I(dir)->chash = namehash; + F2FS_I(dir)->clevel = level; + } + + return de; +} + +/* + * Find an entry in the specified directory with the wanted name. + * It returns the page where the entry was found (as a parameter - res_page), + * and the entry itself. Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. + */ +struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, + struct qstr *child, struct page **res_page) +{ + unsigned long npages = dir_blocks(dir); + struct f2fs_dir_entry *de = NULL; + f2fs_hash_t name_hash; + unsigned int max_depth; + unsigned int level; + + *res_page = NULL; + + if (f2fs_has_inline_dentry(dir)) + return find_in_inline_dir(dir, child, res_page); + + if (npages == 0) + return NULL; + + name_hash = f2fs_dentry_hash(child); + max_depth = F2FS_I(dir)->i_current_depth; + + for (level = 0; level < max_depth; level++) { + de = find_in_level(dir, level, child, name_hash, res_page); + if (de) + break; + } + if (!de && F2FS_I(dir)->chash != name_hash) { + F2FS_I(dir)->chash = name_hash; + F2FS_I(dir)->clevel = level - 1; + } + return de; +} + +struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) +{ + struct page *page; + struct f2fs_dir_entry *de; + struct f2fs_dentry_block *dentry_blk; + + if (f2fs_has_inline_dentry(dir)) + return f2fs_parent_inline_dir(dir, p); + + page = get_lock_data_page(dir, 0); + if (IS_ERR(page)) + return NULL; + + dentry_blk = kmap(page); + de = &dentry_blk->dentry[1]; + *p = page; + unlock_page(page); + return de; +} + +ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) +{ + ino_t res = 0; + struct f2fs_dir_entry *de; + struct page *page; + + de = f2fs_find_entry(dir, qstr, &page); + if (de) { + res = le32_to_cpu(de->ino); + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + } + + return res; +} + +void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, + struct page *page, struct inode *inode) +{ + enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA; + lock_page(page); + f2fs_wait_on_page_writeback(page, type); + de->ino = cpu_to_le32(inode->i_ino); + set_de_type(de, inode->i_mode); + f2fs_dentry_kunmap(dir, page); + set_page_dirty(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + + f2fs_put_page(page, 1); +} + +static void init_dent_inode(const struct qstr *name, struct page *ipage) +{ + struct f2fs_inode *ri; + + f2fs_wait_on_page_writeback(ipage, NODE); + + /* copy name info. to this inode page */ + ri = F2FS_INODE(ipage); + ri->i_namelen = cpu_to_le32(name->len); + memcpy(ri->i_name, name->name, name->len); + set_page_dirty(ipage); +} + +int update_dent_inode(struct inode *inode, const struct qstr *name) +{ + struct page *page; + + page = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(page)) + return PTR_ERR(page); + + init_dent_inode(name, page); + f2fs_put_page(page, 1); + + return 0; +} + +void do_make_empty_dir(struct inode *inode, struct inode *parent, + struct f2fs_dentry_ptr *d) +{ + struct f2fs_dir_entry *de; + + de = &d->dentry[0]; + de->name_len = cpu_to_le16(1); + de->hash_code = 0; + de->ino = cpu_to_le32(inode->i_ino); + memcpy(d->filename[0], ".", 1); + set_de_type(de, inode->i_mode); + + de = &d->dentry[1]; + de->hash_code = 0; + de->name_len = cpu_to_le16(2); + de->ino = cpu_to_le32(parent->i_ino); + memcpy(d->filename[1], "..", 2); + set_de_type(de, parent->i_mode); + + test_and_set_bit_le(0, (void *)d->bitmap); + test_and_set_bit_le(1, (void *)d->bitmap); +} + +static int make_empty_dir(struct inode *inode, + struct inode *parent, struct page *page) +{ + struct page *dentry_page; + struct f2fs_dentry_block *dentry_blk; + struct f2fs_dentry_ptr d; + + if (f2fs_has_inline_dentry(inode)) + return make_empty_inline_dir(inode, parent, page); + + dentry_page = get_new_data_page(inode, page, 0, true); + if (IS_ERR(dentry_page)) + return PTR_ERR(dentry_page); + + dentry_blk = kmap_atomic(dentry_page); + + make_dentry_ptr(&d, (void *)dentry_blk, 1); + do_make_empty_dir(inode, parent, &d); + + kunmap_atomic(dentry_blk); + + set_page_dirty(dentry_page); + f2fs_put_page(dentry_page, 1); + return 0; +} + +struct page *init_inode_metadata(struct inode *inode, struct inode *dir, + const struct qstr *name, struct page *dpage) +{ + struct page *page; + int err; + + if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { + page = new_inode_page(inode); + if (IS_ERR(page)) + return page; + + if (S_ISDIR(inode->i_mode)) { + err = make_empty_dir(inode, dir, page); + if (err) + goto error; + } + + err = f2fs_init_acl(inode, dir, page, dpage); + if (err) + goto put_error; + + err = f2fs_init_security(inode, dir, name, page); + if (err) + goto put_error; + } else { + page = get_node_page(F2FS_I_SB(dir), inode->i_ino); + if (IS_ERR(page)) + return page; + + set_cold_node(inode, page); + } + + if (name) + init_dent_inode(name, page); + + /* + * This file should be checkpointed during fsync. + * We lost i_pino from now on. + */ + if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { + file_lost_pino(inode); + /* + * If link the tmpfile to alias through linkat path, + * we should remove this inode from orphan list. + */ + if (inode->i_nlink == 0) + remove_orphan_inode(F2FS_I_SB(dir), inode->i_ino); + inc_nlink(inode); + } + return page; + +put_error: + f2fs_put_page(page, 1); +error: + /* once the failed inode becomes a bad inode, i_mode is S_IFREG */ + truncate_inode_pages(&inode->i_data, 0); + truncate_blocks(inode, 0, false); + remove_dirty_dir_inode(inode); + remove_inode_page(inode); + return ERR_PTR(err); +} + +void update_parent_metadata(struct inode *dir, struct inode *inode, + unsigned int current_depth) +{ + if (inode && is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { + if (S_ISDIR(inode->i_mode)) { + inc_nlink(dir); + set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); + } + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + + if (F2FS_I(dir)->i_current_depth != current_depth) { + F2FS_I(dir)->i_current_depth = current_depth; + set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + + if (inode && is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) + clear_inode_flag(F2FS_I(inode), FI_INC_LINK); +} + +int room_for_filename(const void *bitmap, int slots, int max_slots) +{ + int bit_start = 0; + int zero_start, zero_end; +next: + zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start); + if (zero_start >= max_slots) + return max_slots; + + zero_end = find_next_bit_le(bitmap, max_slots, zero_start); + if (zero_end - zero_start >= slots) + return zero_start; + + bit_start = zero_end + 1; + + if (zero_end + 1 >= max_slots) + return max_slots; + goto next; +} + +void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *d, + const struct qstr *name, f2fs_hash_t name_hash, + unsigned int bit_pos) +{ + struct f2fs_dir_entry *de; + int slots = GET_DENTRY_SLOTS(name->len); + int i; + + de = &d->dentry[bit_pos]; + de->hash_code = name_hash; + de->name_len = cpu_to_le16(name->len); + memcpy(d->filename[bit_pos], name->name, name->len); + de->ino = cpu_to_le32(ino); + set_de_type(de, mode); + for (i = 0; i < slots; i++) + test_and_set_bit_le(bit_pos + i, (void *)d->bitmap); +} + +/* + * Caller should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op(). + */ +int __f2fs_add_link(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode) +{ + unsigned int bit_pos; + unsigned int level; + unsigned int current_depth; + unsigned long bidx, block; + f2fs_hash_t dentry_hash; + unsigned int nbucket, nblock; + size_t namelen = name->len; + struct page *dentry_page = NULL; + struct f2fs_dentry_block *dentry_blk = NULL; + struct f2fs_dentry_ptr d; + int slots = GET_DENTRY_SLOTS(namelen); + struct page *page = NULL; + int err = 0; + + if (f2fs_has_inline_dentry(dir)) { + err = f2fs_add_inline_entry(dir, name, inode, ino, mode); + if (!err || err != -EAGAIN) + return err; + else + err = 0; + } + + dentry_hash = f2fs_dentry_hash(name); + level = 0; + current_depth = F2FS_I(dir)->i_current_depth; + if (F2FS_I(dir)->chash == dentry_hash) { + level = F2FS_I(dir)->clevel; + F2FS_I(dir)->chash = 0; + } + +start: + if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) + return -ENOSPC; + + /* Increase the depth, if required */ + if (level == current_depth) + ++current_depth; + + nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level); + nblock = bucket_blocks(level); + + bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level, + (le32_to_cpu(dentry_hash) % nbucket)); + + for (block = bidx; block <= (bidx + nblock - 1); block++) { + dentry_page = get_new_data_page(dir, NULL, block, true); + if (IS_ERR(dentry_page)) + return PTR_ERR(dentry_page); + + dentry_blk = kmap(dentry_page); + bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + slots, NR_DENTRY_IN_BLOCK); + if (bit_pos < NR_DENTRY_IN_BLOCK) + goto add_dentry; + + kunmap(dentry_page); + f2fs_put_page(dentry_page, 1); + } + + /* Move to next level to find the empty slot for new dentry */ + ++level; + goto start; +add_dentry: + f2fs_wait_on_page_writeback(dentry_page, DATA); + + if (inode) { + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, name, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + } + + make_dentry_ptr(&d, (void *)dentry_blk, 1); + f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos); + + set_page_dirty(dentry_page); + + if (inode) { + /* we don't need to mark_inode_dirty now */ + F2FS_I(inode)->i_pino = dir->i_ino; + update_inode(inode, page); + f2fs_put_page(page, 1); + } + + update_parent_metadata(dir, inode, current_depth); +fail: + if (inode) + up_write(&F2FS_I(inode)->i_sem); + + if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + update_inode_page(dir); + clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + kunmap(dentry_page); + f2fs_put_page(dentry_page, 1); + return err; +} + +int f2fs_do_tmpfile(struct inode *inode, struct inode *dir) +{ + struct page *page; + int err = 0; + + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, NULL, NULL); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + /* we don't need to mark_inode_dirty now */ + update_inode(inode, page); + f2fs_put_page(page, 1); + + clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); +fail: + up_write(&F2FS_I(inode)->i_sem); + return err; +} + +void f2fs_drop_nlink(struct inode *dir, struct inode *inode, struct page *page) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + + down_write(&F2FS_I(inode)->i_sem); + + if (S_ISDIR(inode->i_mode)) { + drop_nlink(dir); + if (page) + update_inode(dir, page); + else + update_inode_page(dir); + } + inode->i_ctime = CURRENT_TIME; + + drop_nlink(inode); + if (S_ISDIR(inode->i_mode)) { + drop_nlink(inode); + i_size_write(inode, 0); + } + up_write(&F2FS_I(inode)->i_sem); + update_inode_page(inode); + + if (inode->i_nlink == 0) + add_orphan_inode(sbi, inode->i_ino); + else + release_orphan_inode(sbi); +} + +/* + * It only removes the dentry from the dentry page, corresponding name + * entry in name page does not need to be touched during deletion. + */ +void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode) +{ + struct f2fs_dentry_block *dentry_blk; + unsigned int bit_pos; + int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + int i; + + if (f2fs_has_inline_dentry(dir)) + return f2fs_delete_inline_entry(dentry, page, dir, inode); + + lock_page(page); + f2fs_wait_on_page_writeback(page, DATA); + + dentry_blk = page_address(page); + bit_pos = dentry - dentry_blk->dentry; + for (i = 0; i < slots; i++) + test_and_clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); + + /* Let's check and deallocate this dentry page */ + bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_DENTRY_IN_BLOCK, + 0); + kunmap(page); /* kunmap - pair of f2fs_find_entry */ + set_page_dirty(page); + + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + + if (inode) + f2fs_drop_nlink(dir, inode, NULL); + + if (bit_pos == NR_DENTRY_IN_BLOCK) { + truncate_hole(dir, page->index, page->index + 1); + clear_page_dirty_for_io(page); + ClearPagePrivate(page); + ClearPageUptodate(page); + inode_dec_dirty_pages(dir); + } + f2fs_put_page(page, 1); +} + +bool f2fs_empty_dir(struct inode *dir) +{ + unsigned long bidx; + struct page *dentry_page; + unsigned int bit_pos; + struct f2fs_dentry_block *dentry_blk; + unsigned long nblock = dir_blocks(dir); + + if (f2fs_has_inline_dentry(dir)) + return f2fs_empty_inline_dir(dir); + + for (bidx = 0; bidx < nblock; bidx++) { + dentry_page = get_lock_data_page(dir, bidx); + if (IS_ERR(dentry_page)) { + if (PTR_ERR(dentry_page) == -ENOENT) + continue; + else + return false; + } + + dentry_blk = kmap_atomic(dentry_page); + if (bidx == 0) + bit_pos = 2; + else + bit_pos = 0; + bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_DENTRY_IN_BLOCK, + bit_pos); + kunmap_atomic(dentry_blk); + + f2fs_put_page(dentry_page, 1); + + if (bit_pos < NR_DENTRY_IN_BLOCK) + return false; + } + return true; +} + +bool f2fs_fill_dentries(struct file *file, void *dirent, filldir_t filldir, + struct f2fs_dentry_ptr *d, unsigned int n, unsigned int bit_pos) +{ + unsigned int start_bit_pos = bit_pos; + unsigned char d_type; + struct f2fs_dir_entry *de = NULL; + unsigned char *types = f2fs_filetype_table; + int over; + + while (bit_pos < d->max) { + d_type = DT_UNKNOWN; + bit_pos = find_next_bit_le(d->bitmap, d->max, bit_pos); + if (bit_pos >= d->max) + break; + + de = &d->dentry[bit_pos]; + if (types && de->file_type < F2FS_FT_MAX) + d_type = types[de->file_type]; + + over = filldir(dirent, d->filename[bit_pos], + le16_to_cpu(de->name_len), + (n * d->max) + bit_pos, + le32_to_cpu(de->ino), d_type); + if (over) { + file->f_pos += bit_pos - start_bit_pos; + return true; + } + + bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); + } + return false; +} + +static int f2fs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + unsigned long pos = file->f_pos; + unsigned int bit_pos = 0; + struct inode *inode = file_inode(file); + unsigned long npages = dir_blocks(inode); + struct f2fs_dentry_block *dentry_blk = NULL; + struct page *dentry_page = NULL; + struct file_ra_state *ra = &file->f_ra; + struct f2fs_dentry_ptr d; + unsigned int n = 0; + + if (f2fs_has_inline_dentry(inode)) + return f2fs_read_inline_dir(file, dirent, filldir); + + bit_pos = (pos % NR_DENTRY_IN_BLOCK); + n = (pos / NR_DENTRY_IN_BLOCK); + + /* readahead for multi pages of dir */ + if (npages - n > 1 && !ra_has_index(ra, n)) + page_cache_sync_readahead(inode->i_mapping, ra, file, n, + min(npages - n, (pgoff_t)MAX_DIR_RA_PAGES)); + + for (; n < npages; n++) { + dentry_page = get_lock_data_page(inode, n); + if (IS_ERR(dentry_page)) + continue; + + dentry_blk = kmap(dentry_page); + + make_dentry_ptr(&d, (void *)dentry_blk, 1); + + if (f2fs_fill_dentries(file, dirent, filldir, &d, n, bit_pos)) + goto stop; + + bit_pos = 0; + file->f_pos = (n + 1) * NR_DENTRY_IN_BLOCK; + kunmap(dentry_page); + f2fs_put_page(dentry_page, 1); + dentry_page = NULL; + } +stop: + if (dentry_page && !IS_ERR(dentry_page)) { + kunmap(dentry_page); + f2fs_put_page(dentry_page, 1); + } + + return 0; +} + +const struct file_operations f2fs_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = f2fs_readdir, + .fsync = f2fs_sync_file, + .unlocked_ioctl = f2fs_ioctl, +}; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h new file mode 100644 index 000000000000..df94cc338c9c --- /dev/null +++ b/fs/f2fs/f2fs.h @@ -0,0 +1,1819 @@ +/* + * fs/f2fs/f2fs.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _LINUX_F2FS_H +#define _LINUX_F2FS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_F2FS_CHECK_FS +#define f2fs_bug_on(sbi, condition) BUG_ON(condition) +#define f2fs_down_write(x, y) down_write(x) +#else +#define f2fs_bug_on(sbi, condition) \ + do { \ + if (unlikely(condition)) { \ + WARN_ON(1); \ + set_sbi_flag(sbi, SBI_NEED_FSCK); \ + } \ + } while (0) +#define f2fs_down_write(x, y) down_write(x) +#endif + +/* + * For mount options + */ +#define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */ +#define F2FS_MOUNT_BG_GC 0x00000001 +#define F2FS_MOUNT_DISABLE_ROLL_FORWARD 0x00000002 +#define F2FS_MOUNT_DISCARD 0x00000004 +#define F2FS_MOUNT_NOHEAP 0x00000008 +#define F2FS_MOUNT_XATTR_USER 0x00000010 +#define F2FS_MOUNT_POSIX_ACL 0x00000020 +#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 +#define F2FS_MOUNT_INLINE_XATTR 0x00000080 +#define F2FS_MOUNT_INLINE_DATA 0x00000100 +#define F2FS_MOUNT_INLINE_DENTRY 0x00000200 +#define F2FS_MOUNT_FLUSH_MERGE 0x00000400 +#define F2FS_MOUNT_NOBARRIER 0x00000800 +#define F2FS_MOUNT_FASTBOOT 0x00001000 +#define F2FS_MOUNT_EXTENT_CACHE 0x00002000 + +#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) +#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) +#define test_opt(sbi, option) (sbi->mount_opt.opt & F2FS_MOUNT_##option) + +#define ver_after(a, b) (typecheck(unsigned long long, a) && \ + typecheck(unsigned long long, b) && \ + ((long long)((a) - (b)) > 0)) + +typedef u32 block_t; /* + * should not change u32, since it is the on-disk block + * address format, __le32. + */ +typedef u32 nid_t; + +struct f2fs_mount_info { + unsigned int opt; +}; + +#define CRCPOLY_LE 0xedb88320 + +static inline __u32 f2fs_crc32(void *buf, size_t len) +{ + unsigned char *p = (unsigned char *)buf; + __u32 crc = F2FS_SUPER_MAGIC; + int i; + + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} + +static inline bool f2fs_crc_valid(__u32 blk_crc, void *buf, size_t buf_size) +{ + return f2fs_crc32(buf, buf_size) == blk_crc; +} + +/* + * For checkpoint manager + */ +enum { + NAT_BITMAP, + SIT_BITMAP +}; + +enum { + CP_UMOUNT, + CP_FASTBOOT, + CP_SYNC, + CP_RECOVERY, + CP_DISCARD, +}; + +#define DEF_BATCHED_TRIM_SECTIONS 32 +#define BATCHED_TRIM_SEGMENTS(sbi) \ + (SM_I(sbi)->trim_sections * (sbi)->segs_per_sec) + +struct cp_control { + int reason; + __u64 trim_start; + __u64 trim_end; + __u64 trim_minlen; + __u64 trimmed; +}; + +/* + * For CP/NAT/SIT/SSA readahead + */ +enum { + META_CP, + META_NAT, + META_SIT, + META_SSA, + META_POR, +}; + +/* for the list of ino */ +enum { + ORPHAN_INO, /* for orphan ino list */ + APPEND_INO, /* for append ino list */ + UPDATE_INO, /* for update ino list */ + MAX_INO_ENTRY, /* max. list */ +}; + +struct ino_entry { + struct list_head list; /* list head */ + nid_t ino; /* inode number */ +}; + +/* + * for the list of directory inodes or gc inodes. + * NOTE: there are two slab users for this structure, if we add/modify/delete + * fields in structure for one of slab users, it may affect fields or size of + * other one, in this condition, it's better to split both of slab and related + * data structure. + */ +struct inode_entry { + struct list_head list; /* list head */ + struct inode *inode; /* vfs inode pointer */ +}; + +/* for the list of blockaddresses to be discarded */ +struct discard_entry { + struct list_head list; /* list head */ + block_t blkaddr; /* block address to be discarded */ + int len; /* # of consecutive blocks of the discard */ +}; + +/* for the list of fsync inodes, used only during recovery */ +struct fsync_inode_entry { + struct list_head list; /* list head */ + struct inode *inode; /* vfs inode pointer */ + block_t blkaddr; /* block address locating the last fsync */ + block_t last_dentry; /* block address locating the last dentry */ + block_t last_inode; /* block address locating the last inode */ +}; + +#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats)) +#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits)) + +#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne) +#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid) +#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) +#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) + +#define MAX_NAT_JENTRIES(sum) (NAT_JOURNAL_ENTRIES - nats_in_cursum(sum)) +#define MAX_SIT_JENTRIES(sum) (SIT_JOURNAL_ENTRIES - sits_in_cursum(sum)) + +static inline int update_nats_in_cursum(struct f2fs_summary_block *rs, int i) +{ + int before = nats_in_cursum(rs); + rs->n_nats = cpu_to_le16(before + i); + return before; +} + +static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) +{ + int before = sits_in_cursum(rs); + rs->n_sits = cpu_to_le16(before + i); + return before; +} + +static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size, + int type) +{ + if (type == NAT_JOURNAL) + return size <= MAX_NAT_JENTRIES(sum); + return size <= MAX_SIT_JENTRIES(sum); +} + +/* + * ioctl commands + */ +#define F2FS_IOC_GETFLAGS FS_IOC_GETFLAGS +#define F2FS_IOC_SETFLAGS FS_IOC_SETFLAGS +#define F2FS_IOC_GETVERSION FS_IOC_GETVERSION + +#define F2FS_IOCTL_MAGIC 0xf5 +#define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) +#define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) +#define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) +#define F2FS_IOC_RELEASE_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 4) +#define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) + +/* + * should be same as XFS_IOC_GOINGDOWN. + * Flags for going down operation used by FS_IOC_GOINGDOWN + */ +#define F2FS_IOC_SHUTDOWN _IOR('X', 125, __u32) /* Shutdown */ +#define F2FS_GOING_DOWN_FULLSYNC 0x0 /* going down with full sync */ +#define F2FS_GOING_DOWN_METASYNC 0x1 /* going down with metadata */ +#define F2FS_GOING_DOWN_NOSYNC 0x2 /* going down */ + +#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +/* + * ioctl commands in 32 bit emulation + */ +#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS +#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#endif + +/* + * For INODE and NODE manager + */ +/* for directory operations */ +struct f2fs_dentry_ptr { + const void *bitmap; + struct f2fs_dir_entry *dentry; + __u8 (*filename)[F2FS_SLOT_LEN]; + int max; +}; + +static inline void make_dentry_ptr(struct f2fs_dentry_ptr *d, + void *src, int type) +{ + if (type == 1) { + struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src; + d->max = NR_DENTRY_IN_BLOCK; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } else { + struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src; + d->max = NR_INLINE_DENTRY; + d->bitmap = &t->dentry_bitmap; + d->dentry = t->dentry; + d->filename = t->filename; + } +} + +/* + * XATTR_NODE_OFFSET stores xattrs to one node block per file keeping -1 + * as its node offset to distinguish from index node blocks. + * But some bits are used to mark the node block. + */ +#define XATTR_NODE_OFFSET ((((unsigned int)-1) << OFFSET_BIT_SHIFT) \ + >> OFFSET_BIT_SHIFT) +enum { + ALLOC_NODE, /* allocate a new node page if needed */ + LOOKUP_NODE, /* look up a node without readahead */ + LOOKUP_NODE_RA, /* + * look up a node with readahead called + * by get_data_block. + */ +}; + +#define F2FS_LINK_MAX 32000 /* maximum link count per file */ + +#define MAX_DIR_RA_PAGES 4 /* maximum ra pages of dir */ + +/* vector size for gang look-up from extent cache that consists of radix tree */ +#define EXT_TREE_VEC_SIZE 64 + +/* for in-memory extent cache entry */ +#define F2FS_MIN_EXTENT_LEN 64 /* minimum extent length */ + +/* number of extent info in extent cache we try to shrink */ +#define EXTENT_CACHE_SHRINK_NUMBER 128 + +struct extent_info { + unsigned int fofs; /* start offset in a file */ + u32 blk; /* start block address of the extent */ + unsigned int len; /* length of the extent */ +}; + +struct extent_node { + struct rb_node rb_node; /* rb node located in rb-tree */ + struct list_head list; /* node in global extent list of sbi */ + struct extent_info ei; /* extent info */ +}; + +struct extent_tree { + nid_t ino; /* inode number */ + struct rb_root root; /* root of extent info rb-tree */ + struct extent_node *cached_en; /* recently accessed extent node */ + rwlock_t lock; /* protect extent info rb-tree */ + atomic_t refcount; /* reference count of rb-tree */ + unsigned int count; /* # of extent node in rb-tree*/ +}; + +/* + * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. + */ +#define FADVISE_COLD_BIT 0x01 +#define FADVISE_LOST_PINO_BIT 0x02 + +#define DEF_DIR_LEVEL 0 + +struct f2fs_inode_info { + struct inode vfs_inode; /* serve a vfs inode */ + unsigned long i_flags; /* keep an inode flags for ioctl */ + unsigned char i_advise; /* use to give file attribute hints */ + unsigned char i_dir_level; /* use for dentry level for large dir */ + unsigned int i_current_depth; /* use only in directory structure */ + unsigned int i_pino; /* parent inode number */ + umode_t i_acl_mode; /* keep file acl mode temporarily */ + + /* Use below internally in f2fs*/ + unsigned long flags; /* use to pass per-file flags */ + struct rw_semaphore i_sem; /* protect fi info */ + atomic_t dirty_pages; /* # of dirty pages */ + f2fs_hash_t chash; /* hash value of given file name */ + unsigned int clevel; /* maximum level of given file name */ + nid_t i_xattr_nid; /* node id that contains xattrs */ + unsigned long long xattr_ver; /* cp version of xattr modification */ + struct extent_info ext; /* in-memory extent cache entry */ + rwlock_t ext_lock; /* rwlock for single extent cache */ + struct inode_entry *dirty_dir; /* the pointer of dirty dir */ + + struct radix_tree_root inmem_root; /* radix tree for inmem pages */ + struct list_head inmem_pages; /* inmemory pages managed by f2fs */ + struct mutex inmem_lock; /* lock for inmemory pages */ +}; + +static inline void get_extent_info(struct extent_info *ext, + struct f2fs_extent i_ext) +{ + ext->fofs = le32_to_cpu(i_ext.fofs); + ext->blk = le32_to_cpu(i_ext.blk); + ext->len = le32_to_cpu(i_ext.len); +} + +static inline void set_raw_extent(struct extent_info *ext, + struct f2fs_extent *i_ext) +{ + i_ext->fofs = cpu_to_le32(ext->fofs); + i_ext->blk = cpu_to_le32(ext->blk); + i_ext->len = cpu_to_le32(ext->len); +} + +static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, + u32 blk, unsigned int len) +{ + ei->fofs = fofs; + ei->blk = blk; + ei->len = len; +} + +static inline bool __is_extent_same(struct extent_info *ei1, + struct extent_info *ei2) +{ + return (ei1->fofs == ei2->fofs && ei1->blk == ei2->blk && + ei1->len == ei2->len); +} + +static inline bool __is_extent_mergeable(struct extent_info *back, + struct extent_info *front) +{ + return (back->fofs + back->len == front->fofs && + back->blk + back->len == front->blk); +} + +static inline bool __is_back_mergeable(struct extent_info *cur, + struct extent_info *back) +{ + return __is_extent_mergeable(back, cur); +} + +static inline bool __is_front_mergeable(struct extent_info *cur, + struct extent_info *front) +{ + return __is_extent_mergeable(cur, front); +} + +struct f2fs_nm_info { + block_t nat_blkaddr; /* base disk address of NAT */ + nid_t max_nid; /* maximum possible node ids */ + nid_t available_nids; /* maximum available node ids */ + nid_t next_scan_nid; /* the next nid to be scanned */ + unsigned int ram_thresh; /* control the memory footprint */ + + /* NAT cache management */ + struct radix_tree_root nat_root;/* root of the nat entry cache */ + struct radix_tree_root nat_set_root;/* root of the nat set cache */ + struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */ + struct list_head nat_entries; /* cached nat entry list (clean) */ + unsigned int nat_cnt; /* the # of cached nat entries */ + unsigned int dirty_nat_cnt; /* total num of nat entries in set */ + + /* free node ids management */ + struct radix_tree_root free_nid_root;/* root of the free_nid cache */ + struct list_head free_nid_list; /* a list for free nids */ + spinlock_t free_nid_list_lock; /* protect free nid list */ + unsigned int fcnt; /* the number of free node id */ + struct mutex build_lock; /* lock for build free nids */ + + /* for checkpoint */ + char *nat_bitmap; /* NAT bitmap pointer */ + int bitmap_size; /* bitmap size */ +}; + +/* + * this structure is used as one of function parameters. + * all the information are dedicated to a given direct node block determined + * by the data offset in a file. + */ +struct dnode_of_data { + struct inode *inode; /* vfs inode pointer */ + struct page *inode_page; /* its inode page, NULL is possible */ + struct page *node_page; /* cached direct node page */ + nid_t nid; /* node id of the direct node block */ + unsigned int ofs_in_node; /* data offset in the node page */ + bool inode_page_locked; /* inode page is locked or not */ + block_t data_blkaddr; /* block address of the node block */ +}; + +static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode, + struct page *ipage, struct page *npage, nid_t nid) +{ + memset(dn, 0, sizeof(*dn)); + dn->inode = inode; + dn->inode_page = ipage; + dn->node_page = npage; + dn->nid = nid; +} + +/* + * For SIT manager + * + * By default, there are 6 active log areas across the whole main area. + * When considering hot and cold data separation to reduce cleaning overhead, + * we split 3 for data logs and 3 for node logs as hot, warm, and cold types, + * respectively. + * In the current design, you should not change the numbers intentionally. + * Instead, as a mount option such as active_logs=x, you can use 2, 4, and 6 + * logs individually according to the underlying devices. (default: 6) + * Just in case, on-disk layout covers maximum 16 logs that consist of 8 for + * data and 8 for node logs. + */ +#define NR_CURSEG_DATA_TYPE (3) +#define NR_CURSEG_NODE_TYPE (3) +#define NR_CURSEG_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) + +enum { + CURSEG_HOT_DATA = 0, /* directory entry blocks */ + CURSEG_WARM_DATA, /* data blocks */ + CURSEG_COLD_DATA, /* multimedia or GCed data blocks */ + CURSEG_HOT_NODE, /* direct node blocks of directory files */ + CURSEG_WARM_NODE, /* direct node blocks of normal files */ + CURSEG_COLD_NODE, /* indirect node blocks */ + NO_CHECK_TYPE, + CURSEG_DIRECT_IO, /* to use for the direct IO path */ +}; + +struct flush_cmd { + struct completion wait; + struct llist_node llnode; + int ret; +}; + +struct flush_cmd_control { + struct task_struct *f2fs_issue_flush; /* flush thread */ + wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */ + struct llist_head issue_list; /* list for command issue */ + struct llist_node *dispatch_list; /* list for command dispatch */ +}; + +struct f2fs_sm_info { + struct sit_info *sit_info; /* whole segment information */ + struct free_segmap_info *free_info; /* free segment information */ + struct dirty_seglist_info *dirty_info; /* dirty segment information */ + struct curseg_info *curseg_array; /* active segment information */ + + block_t seg0_blkaddr; /* block address of 0'th segment */ + block_t main_blkaddr; /* start block address of main area */ + block_t ssa_blkaddr; /* start block address of SSA area */ + + unsigned int segment_count; /* total # of segments */ + unsigned int main_segments; /* # of segments in main area */ + unsigned int reserved_segments; /* # of reserved segments */ + unsigned int ovp_segments; /* # of overprovision segments */ + + /* a threshold to reclaim prefree segments */ + unsigned int rec_prefree_segments; + + /* for small discard management */ + struct list_head discard_list; /* 4KB discard list */ + int nr_discards; /* # of discards in the list */ + int max_discards; /* max. discards to be issued */ + + /* for batched trimming */ + unsigned int trim_sections; /* # of sections to trim */ + + struct list_head sit_entry_set; /* sit entry set list */ + + unsigned int ipu_policy; /* in-place-update policy */ + unsigned int min_ipu_util; /* in-place-update threshold */ + unsigned int min_fsync_blocks; /* threshold for fsync */ + + /* for flush command control */ + struct flush_cmd_control *cmd_control_info; + +}; + +/* + * For superblock + */ +/* + * COUNT_TYPE for monitoring + * + * f2fs monitors the number of several block types such as on-writeback, + * dirty dentry blocks, dirty node blocks, and dirty meta blocks. + */ +enum count_type { + F2FS_WRITEBACK, + F2FS_DIRTY_DENTS, + F2FS_DIRTY_NODES, + F2FS_DIRTY_META, + F2FS_INMEM_PAGES, + NR_COUNT_TYPE, +}; + +/* + * The below are the page types of bios used in submit_bio(). + * The available types are: + * DATA User data pages. It operates as async mode. + * NODE Node pages. It operates as async mode. + * META FS metadata pages such as SIT, NAT, CP. + * NR_PAGE_TYPE The number of page types. + * META_FLUSH Make sure the previous pages are written + * with waiting the bio's completion + * ... Only can be used with META. + */ +#define PAGE_TYPE_OF_BIO(type) ((type) > META ? META : (type)) +enum page_type { + DATA, + NODE, + META, + NR_PAGE_TYPE, + META_FLUSH, + INMEM, /* the below types are used by tracepoints only. */ + INMEM_DROP, + IPU, + OPU, +}; + +struct f2fs_io_info { + enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ + int rw; /* contains R/RS/W/WS with REQ_META/REQ_PRIO */ + block_t blk_addr; /* block address to be written */ +}; + +#define is_read_io(rw) (((rw) & 1) == READ) +struct f2fs_bio_info { + struct f2fs_sb_info *sbi; /* f2fs superblock */ + struct bio *bio; /* bios to merge */ + sector_t last_block_in_bio; /* last block number */ + struct f2fs_io_info fio; /* store buffered io info. */ + struct rw_semaphore io_rwsem; /* blocking op for bio */ +}; + +/* for inner inode cache management */ +struct inode_management { + struct radix_tree_root ino_root; /* ino entry array */ + spinlock_t ino_lock; /* for ino entry lock */ + struct list_head ino_list; /* inode list head */ + unsigned long ino_num; /* number of entries */ +}; + +/* For s_flag in struct f2fs_sb_info */ +enum { + SBI_IS_DIRTY, /* dirty flag for checkpoint */ + SBI_IS_CLOSE, /* specify unmounting */ + SBI_NEED_FSCK, /* need fsck.f2fs to fix */ + SBI_POR_DOING, /* recovery is doing or not */ +}; + +struct f2fs_sb_info { + struct super_block *sb; /* pointer to VFS super block */ + struct proc_dir_entry *s_proc; /* proc entry */ + struct buffer_head *raw_super_buf; /* buffer head of raw sb */ + struct f2fs_super_block *raw_super; /* raw super block pointer */ + int s_flag; /* flags for sbi */ + + /* for node-related operations */ + struct f2fs_nm_info *nm_info; /* node manager */ + struct inode *node_inode; /* cache node blocks */ + + /* for segment-related operations */ + struct f2fs_sm_info *sm_info; /* segment manager */ + + /* for bio operations */ + struct f2fs_bio_info read_io; /* for read bios */ + struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ + + /* for checkpoint */ + struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ + struct inode *meta_inode; /* cache meta blocks */ + struct mutex cp_mutex; /* checkpoint procedure lock */ + struct rw_semaphore cp_rwsem; /* blocking FS operations */ + struct rw_semaphore node_write; /* locking node writes */ + wait_queue_head_t cp_wait; + + struct inode_management im[MAX_INO_ENTRY]; /* manage inode cache */ + + /* for orphan inode, use 0'th array */ + unsigned int max_orphans; /* max orphan inodes */ + + /* for directory inode management */ + struct list_head dir_inode_list; /* dir inode list */ + spinlock_t dir_inode_lock; /* for dir inode list lock */ + + /* for extent tree cache */ + struct radix_tree_root extent_tree_root;/* cache extent cache entries */ + struct rw_semaphore extent_tree_lock; /* locking extent radix tree */ + struct list_head extent_list; /* lru list for shrinker */ + spinlock_t extent_lock; /* locking extent lru list */ + int total_ext_tree; /* extent tree count */ + atomic_t total_ext_node; /* extent info count */ + + /* basic filesystem units */ + unsigned int log_sectors_per_block; /* log2 sectors per block */ + unsigned int log_blocksize; /* log2 block size */ + unsigned int blocksize; /* block size */ + unsigned int root_ino_num; /* root inode number*/ + unsigned int node_ino_num; /* node inode number*/ + unsigned int meta_ino_num; /* meta inode number*/ + unsigned int log_blocks_per_seg; /* log2 blocks per segment */ + unsigned int blocks_per_seg; /* blocks per segment */ + unsigned int segs_per_sec; /* segments per section */ + unsigned int secs_per_zone; /* sections per zone */ + unsigned int total_sections; /* total section count */ + unsigned int total_node_count; /* total node block count */ + unsigned int total_valid_node_count; /* valid node block count */ + unsigned int total_valid_inode_count; /* valid inode count */ + int active_logs; /* # of active logs */ + int dir_level; /* directory level */ + + block_t user_block_count; /* # of user blocks */ + block_t total_valid_block_count; /* # of valid blocks */ + block_t alloc_valid_block_count; /* # of allocated blocks */ + block_t last_valid_block_count; /* for recovery */ + u32 s_next_generation; /* for NFS support */ + atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ + + struct f2fs_mount_info mount_opt; /* mount options */ + + /* for cleaning operations */ + struct mutex gc_mutex; /* mutex for GC */ + struct f2fs_gc_kthread *gc_thread; /* GC thread */ + unsigned int cur_victim_sec; /* current victim section num */ + + /* maximum # of trials to find a victim segment for SSR and GC */ + unsigned int max_victim_search; + + /* + * for stat information. + * one is for the LFS mode, and the other is for the SSR mode. + */ +#ifdef CONFIG_F2FS_STAT_FS + struct f2fs_stat_info *stat_info; /* FS status information */ + unsigned int segment_count[2]; /* # of allocated segments */ + unsigned int block_count[2]; /* # of allocated blocks */ + atomic_t inplace_count; /* # of inplace update */ + int total_hit_ext, read_hit_ext; /* extent cache hit ratio */ + atomic_t inline_inode; /* # of inline_data inodes */ + atomic_t inline_dir; /* # of inline_dentry inodes */ + int bg_gc; /* background gc calls */ + unsigned int n_dirty_dirs; /* # of dir inodes */ +#endif + unsigned int last_victim[2]; /* last victim segment # */ + spinlock_t stat_lock; /* lock for stat operations */ + + /* For sysfs suppport */ + struct kobject s_kobj; + struct completion s_kobj_unregister; +}; + +/* + * Inline functions + */ +static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) +{ + return container_of(inode, struct f2fs_inode_info, vfs_inode); +} + +static inline struct f2fs_sb_info *F2FS_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct f2fs_sb_info *F2FS_I_SB(struct inode *inode) +{ + return F2FS_SB(inode->i_sb); +} + +static inline struct f2fs_sb_info *F2FS_M_SB(struct address_space *mapping) +{ + return F2FS_I_SB(mapping->host); +} + +static inline struct f2fs_sb_info *F2FS_P_SB(struct page *page) +{ + return F2FS_M_SB(page->mapping); +} + +static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi) +{ + return (struct f2fs_super_block *)(sbi->raw_super); +} + +static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi) +{ + return (struct f2fs_checkpoint *)(sbi->ckpt); +} + +static inline struct f2fs_node *F2FS_NODE(struct page *page) +{ + return (struct f2fs_node *)page_address(page); +} + +static inline struct f2fs_inode *F2FS_INODE(struct page *page) +{ + return &((struct f2fs_node *)page_address(page))->i; +} + +static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi) +{ + return (struct f2fs_nm_info *)(sbi->nm_info); +} + +static inline struct f2fs_sm_info *SM_I(struct f2fs_sb_info *sbi) +{ + return (struct f2fs_sm_info *)(sbi->sm_info); +} + +static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi) +{ + return (struct sit_info *)(SM_I(sbi)->sit_info); +} + +static inline struct free_segmap_info *FREE_I(struct f2fs_sb_info *sbi) +{ + return (struct free_segmap_info *)(SM_I(sbi)->free_info); +} + +static inline struct dirty_seglist_info *DIRTY_I(struct f2fs_sb_info *sbi) +{ + return (struct dirty_seglist_info *)(SM_I(sbi)->dirty_info); +} + +static inline struct address_space *META_MAPPING(struct f2fs_sb_info *sbi) +{ + return sbi->meta_inode->i_mapping; +} + +static inline struct address_space *NODE_MAPPING(struct f2fs_sb_info *sbi) +{ + return sbi->node_inode->i_mapping; +} + +static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type) +{ + return sbi->s_flag & (0x01 << type); +} + +static inline void set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) +{ + sbi->s_flag |= (0x01 << type); +} + +static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type) +{ + sbi->s_flag &= ~(0x01 << type); +} + +static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) +{ + return le64_to_cpu(cp->checkpoint_ver); +} + +static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +{ + unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + return ckpt_flags & f; +} + +static inline void set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +{ + unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + ckpt_flags |= f; + cp->ckpt_flags = cpu_to_le32(ckpt_flags); +} + +static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) +{ + unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); + ckpt_flags &= (~f); + cp->ckpt_flags = cpu_to_le32(ckpt_flags); +} + +static inline void f2fs_lock_op(struct f2fs_sb_info *sbi) +{ + down_read(&sbi->cp_rwsem); +} + +static inline void f2fs_unlock_op(struct f2fs_sb_info *sbi) +{ + up_read(&sbi->cp_rwsem); +} + +static inline void f2fs_lock_all(struct f2fs_sb_info *sbi) +{ + f2fs_down_write(&sbi->cp_rwsem, &sbi->cp_mutex); +} + +static inline void f2fs_unlock_all(struct f2fs_sb_info *sbi) +{ + up_write(&sbi->cp_rwsem); +} + +static inline int __get_cp_reason(struct f2fs_sb_info *sbi) +{ + int reason = CP_SYNC; + + if (test_opt(sbi, FASTBOOT)) + reason = CP_FASTBOOT; + if (is_sbi_flag_set(sbi, SBI_IS_CLOSE)) + reason = CP_UMOUNT; + return reason; +} + +static inline bool __remain_node_summaries(int reason) +{ + return (reason == CP_UMOUNT || reason == CP_FASTBOOT); +} + +static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) +{ + return (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) || + is_set_ckpt_flags(F2FS_CKPT(sbi), CP_FASTBOOT_FLAG)); +} + +/* + * Check whether the given nid is within node id range. + */ +static inline int check_nid_range(struct f2fs_sb_info *sbi, nid_t nid) +{ + if (unlikely(nid < F2FS_ROOT_INO(sbi))) + return -EINVAL; + if (unlikely(nid >= NM_I(sbi)->max_nid)) + return -EINVAL; + return 0; +} + +#define F2FS_DEFAULT_ALLOCATED_BLOCKS 1 + +/* + * Check whether the inode has blocks or not + */ +static inline int F2FS_HAS_BLOCKS(struct inode *inode) +{ + if (F2FS_I(inode)->i_xattr_nid) + return inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS + 1; + else + return inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS; +} + +static inline bool f2fs_has_xattr_block(unsigned int ofs) +{ + return ofs == XATTR_NODE_OFFSET; +} + +static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, + struct inode *inode, blkcnt_t count) +{ + block_t valid_block_count; + + spin_lock(&sbi->stat_lock); + valid_block_count = + sbi->total_valid_block_count + (block_t)count; + if (unlikely(valid_block_count > sbi->user_block_count)) { + spin_unlock(&sbi->stat_lock); + return false; + } + inode->i_blocks += count; + sbi->total_valid_block_count = valid_block_count; + sbi->alloc_valid_block_count += (block_t)count; + spin_unlock(&sbi->stat_lock); + return true; +} + +static inline void dec_valid_block_count(struct f2fs_sb_info *sbi, + struct inode *inode, + blkcnt_t count) +{ + spin_lock(&sbi->stat_lock); + f2fs_bug_on(sbi, sbi->total_valid_block_count < (block_t) count); + f2fs_bug_on(sbi, inode->i_blocks < count); + inode->i_blocks -= count; + sbi->total_valid_block_count -= (block_t)count; + spin_unlock(&sbi->stat_lock); +} + +static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) +{ + atomic_inc(&sbi->nr_pages[count_type]); + set_sbi_flag(sbi, SBI_IS_DIRTY); +} + +static inline void inode_inc_dirty_pages(struct inode *inode) +{ + atomic_inc(&F2FS_I(inode)->dirty_pages); + if (S_ISDIR(inode->i_mode)) + inc_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS); +} + +static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) +{ + atomic_dec(&sbi->nr_pages[count_type]); +} + +static inline void inode_dec_dirty_pages(struct inode *inode) +{ + if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) + return; + + atomic_dec(&F2FS_I(inode)->dirty_pages); + + if (S_ISDIR(inode->i_mode)) + dec_page_count(F2FS_I_SB(inode), F2FS_DIRTY_DENTS); +} + +static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) +{ + return atomic_read(&sbi->nr_pages[count_type]); +} + +static inline int get_dirty_pages(struct inode *inode) +{ + return atomic_read(&F2FS_I(inode)->dirty_pages); +} + +static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) +{ + unsigned int pages_per_sec = sbi->segs_per_sec * + (1 << sbi->log_blocks_per_seg); + return ((get_pages(sbi, block_type) + pages_per_sec - 1) + >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; +} + +static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) +{ + return sbi->total_valid_block_count; +} + +static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + + /* return NAT or SIT bitmap */ + if (flag == NAT_BITMAP) + return le32_to_cpu(ckpt->nat_ver_bitmap_bytesize); + else if (flag == SIT_BITMAP) + return le32_to_cpu(ckpt->sit_ver_bitmap_bytesize); + + return 0; +} + +static inline block_t __cp_payload(struct f2fs_sb_info *sbi) +{ + return le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_payload); +} + +static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + int offset; + + if (__cp_payload(sbi) > 0) { + if (flag == NAT_BITMAP) + return &ckpt->sit_nat_version_bitmap; + else + return (unsigned char *)ckpt + F2FS_BLKSIZE; + } else { + offset = (flag == NAT_BITMAP) ? + le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0; + return &ckpt->sit_nat_version_bitmap + offset; + } +} + +static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi) +{ + block_t start_addr; + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + unsigned long long ckpt_version = cur_cp_version(ckpt); + + start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); + + /* + * odd numbered checkpoint should at cp segment 0 + * and even segment must be at cp segment 1 + */ + if (!(ckpt_version & 1)) + start_addr += sbi->blocks_per_seg; + + return start_addr; +} + +static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) +{ + return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); +} + +static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, + struct inode *inode) +{ + block_t valid_block_count; + unsigned int valid_node_count; + + spin_lock(&sbi->stat_lock); + + valid_block_count = sbi->total_valid_block_count + 1; + if (unlikely(valid_block_count > sbi->user_block_count)) { + spin_unlock(&sbi->stat_lock); + return false; + } + + valid_node_count = sbi->total_valid_node_count + 1; + if (unlikely(valid_node_count > sbi->total_node_count)) { + spin_unlock(&sbi->stat_lock); + return false; + } + + if (inode) + inode->i_blocks++; + + sbi->alloc_valid_block_count++; + sbi->total_valid_node_count++; + sbi->total_valid_block_count++; + spin_unlock(&sbi->stat_lock); + + return true; +} + +static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, + struct inode *inode) +{ + spin_lock(&sbi->stat_lock); + + f2fs_bug_on(sbi, !sbi->total_valid_block_count); + f2fs_bug_on(sbi, !sbi->total_valid_node_count); + f2fs_bug_on(sbi, !inode->i_blocks); + + inode->i_blocks--; + sbi->total_valid_node_count--; + sbi->total_valid_block_count--; + + spin_unlock(&sbi->stat_lock); +} + +static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi) +{ + return sbi->total_valid_node_count; +} + +static inline void inc_valid_inode_count(struct f2fs_sb_info *sbi) +{ + spin_lock(&sbi->stat_lock); + f2fs_bug_on(sbi, sbi->total_valid_inode_count == sbi->total_node_count); + sbi->total_valid_inode_count++; + spin_unlock(&sbi->stat_lock); +} + +static inline void dec_valid_inode_count(struct f2fs_sb_info *sbi) +{ + spin_lock(&sbi->stat_lock); + f2fs_bug_on(sbi, !sbi->total_valid_inode_count); + sbi->total_valid_inode_count--; + spin_unlock(&sbi->stat_lock); +} + +static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi) +{ + return sbi->total_valid_inode_count; +} + +static inline void f2fs_put_page(struct page *page, int unlock) +{ + if (!page) + return; + + if (unlock) { + f2fs_bug_on(F2FS_P_SB(page), !PageLocked(page)); + unlock_page(page); + } + page_cache_release(page); +} + +static inline void f2fs_put_dnode(struct dnode_of_data *dn) +{ + if (dn->node_page) + f2fs_put_page(dn->node_page, 1); + if (dn->inode_page && dn->node_page != dn->inode_page) + f2fs_put_page(dn->inode_page, 0); + dn->node_page = NULL; + dn->inode_page = NULL; +} + +static inline struct kmem_cache *f2fs_kmem_cache_create(const char *name, + size_t size) +{ + return kmem_cache_create(name, size, 0, SLAB_RECLAIM_ACCOUNT, NULL); +} + +static inline void *f2fs_kmem_cache_alloc(struct kmem_cache *cachep, + gfp_t flags) +{ + void *entry; +retry: + entry = kmem_cache_alloc(cachep, flags); + if (!entry) { + cond_resched(); + goto retry; + } + + return entry; +} + +static inline void f2fs_radix_tree_insert(struct radix_tree_root *root, + unsigned long index, void *item) +{ + while (radix_tree_insert(root, index, item)) + cond_resched(); +} + +#define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino) + +static inline bool IS_INODE(struct page *page) +{ + struct f2fs_node *p = F2FS_NODE(page); + return RAW_IS_INODE(p); +} + +static inline __le32 *blkaddr_in_node(struct f2fs_node *node) +{ + return RAW_IS_INODE(node) ? node->i.i_addr : node->dn.addr; +} + +static inline block_t datablock_addr(struct page *node_page, + unsigned int offset) +{ + struct f2fs_node *raw_node; + __le32 *addr_array; + raw_node = F2FS_NODE(node_page); + addr_array = blkaddr_in_node(raw_node); + return le32_to_cpu(addr_array[offset]); +} + +static inline int f2fs_test_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + return mask & *addr; +} + +static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr) +{ + int mask; + int ret; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + ret = mask & *addr; + *addr |= mask; + return ret; +} + +static inline int f2fs_test_and_clear_bit(unsigned int nr, char *addr) +{ + int mask; + int ret; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + ret = mask & *addr; + *addr &= ~mask; + return ret; +} + +static inline void f2fs_change_bit(unsigned int nr, char *addr) +{ + int mask; + + addr += (nr >> 3); + mask = 1 << (7 - (nr & 0x07)); + *addr ^= mask; +} + +/* used for f2fs_inode_info->flags */ +enum { + FI_NEW_INODE, /* indicate newly allocated inode */ + FI_DIRTY_INODE, /* indicate inode is dirty or not */ + FI_DIRTY_DIR, /* indicate directory has dirty pages */ + FI_INC_LINK, /* need to increment i_nlink */ + FI_ACL_MODE, /* indicate acl mode */ + FI_NO_ALLOC, /* should not allocate any blocks */ + FI_UPDATE_DIR, /* should update inode block for consistency */ + FI_DELAY_IPUT, /* used for the recovery */ + FI_NO_EXTENT, /* not to use the extent cache */ + FI_INLINE_XATTR, /* used for inline xattr */ + FI_INLINE_DATA, /* used for inline data*/ + FI_INLINE_DENTRY, /* used for inline dentry */ + FI_APPEND_WRITE, /* inode has appended data */ + FI_UPDATE_WRITE, /* inode has in-place-update data */ + FI_NEED_IPU, /* used for ipu per file */ + FI_ATOMIC_FILE, /* indicate atomic file */ + FI_VOLATILE_FILE, /* indicate volatile file */ + FI_FIRST_BLOCK_WRITTEN, /* indicate #0 data block was written */ + FI_DROP_CACHE, /* drop dirty page cache */ + FI_DATA_EXIST, /* indicate data exists */ + FI_INLINE_DOTS, /* indicate inline dot dentries */ +}; + +static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) +{ + if (!test_bit(flag, &fi->flags)) + set_bit(flag, &fi->flags); +} + +static inline int is_inode_flag_set(struct f2fs_inode_info *fi, int flag) +{ + return test_bit(flag, &fi->flags); +} + +static inline void clear_inode_flag(struct f2fs_inode_info *fi, int flag) +{ + if (test_bit(flag, &fi->flags)) + clear_bit(flag, &fi->flags); +} + +static inline void set_acl_inode(struct f2fs_inode_info *fi, umode_t mode) +{ + fi->i_acl_mode = mode; + set_inode_flag(fi, FI_ACL_MODE); +} + +static inline void get_inline_info(struct f2fs_inode_info *fi, + struct f2fs_inode *ri) +{ + if (ri->i_inline & F2FS_INLINE_XATTR) + set_inode_flag(fi, FI_INLINE_XATTR); + if (ri->i_inline & F2FS_INLINE_DATA) + set_inode_flag(fi, FI_INLINE_DATA); + if (ri->i_inline & F2FS_INLINE_DENTRY) + set_inode_flag(fi, FI_INLINE_DENTRY); + if (ri->i_inline & F2FS_DATA_EXIST) + set_inode_flag(fi, FI_DATA_EXIST); + if (ri->i_inline & F2FS_INLINE_DOTS) + set_inode_flag(fi, FI_INLINE_DOTS); +} + +static inline void set_raw_inline(struct f2fs_inode_info *fi, + struct f2fs_inode *ri) +{ + ri->i_inline = 0; + + if (is_inode_flag_set(fi, FI_INLINE_XATTR)) + ri->i_inline |= F2FS_INLINE_XATTR; + if (is_inode_flag_set(fi, FI_INLINE_DATA)) + ri->i_inline |= F2FS_INLINE_DATA; + if (is_inode_flag_set(fi, FI_INLINE_DENTRY)) + ri->i_inline |= F2FS_INLINE_DENTRY; + if (is_inode_flag_set(fi, FI_DATA_EXIST)) + ri->i_inline |= F2FS_DATA_EXIST; + if (is_inode_flag_set(fi, FI_INLINE_DOTS)) + ri->i_inline |= F2FS_INLINE_DOTS; +} + +static inline int f2fs_has_inline_xattr(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR); +} + +static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi) +{ + if (f2fs_has_inline_xattr(&fi->vfs_inode)) + return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; + return DEF_ADDRS_PER_INODE; +} + +static inline void *inline_xattr_addr(struct page *page) +{ + struct f2fs_inode *ri = F2FS_INODE(page); + return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - + F2FS_INLINE_XATTR_ADDRS]); +} + +static inline int inline_xattr_size(struct inode *inode) +{ + if (f2fs_has_inline_xattr(inode)) + return F2FS_INLINE_XATTR_ADDRS << 2; + else + return 0; +} + +static inline int f2fs_has_inline_data(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DATA); +} + +static inline void f2fs_clear_inline_inode(struct inode *inode) +{ + clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + clear_inode_flag(F2FS_I(inode), FI_DATA_EXIST); +} + +static inline int f2fs_exist_data(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_DATA_EXIST); +} + +static inline int f2fs_has_inline_dots(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DOTS); +} + +static inline bool f2fs_is_atomic_file(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_ATOMIC_FILE); +} + +static inline bool f2fs_is_volatile_file(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_VOLATILE_FILE); +} + +static inline bool f2fs_is_first_block_written(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); +} + +static inline bool f2fs_is_drop_cache(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_DROP_CACHE); +} + +static inline void *inline_data_addr(struct page *page) +{ + struct f2fs_inode *ri = F2FS_INODE(page); + return (void *)&(ri->i_addr[1]); +} + +static inline int f2fs_has_inline_dentry(struct inode *inode) +{ + return is_inode_flag_set(F2FS_I(inode), FI_INLINE_DENTRY); +} + +static inline void f2fs_dentry_kunmap(struct inode *dir, struct page *page) +{ + if (!f2fs_has_inline_dentry(dir)) + kunmap(page); +} + +static inline int f2fs_readonly(struct super_block *sb) +{ + return sb->s_flags & MS_RDONLY; +} + +static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi) +{ + return is_set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); +} + +static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) +{ + set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + sbi->sb->s_flags |= MS_RDONLY; +} + +static inline struct inode *file_inode(struct file *f) +{ + return f->f_path.dentry->d_inode; +} + +#define get_inode_mode(i) \ + ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ + (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) + +/* get offset of first page in next direct node */ +#define PGOFS_OF_NEXT_DNODE(pgofs, fi) \ + ((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \ + (pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \ + ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi)) + +/* + * file.c + */ +int f2fs_sync_file(struct file *, loff_t, loff_t, int); +void truncate_data_blocks(struct dnode_of_data *); +int truncate_blocks(struct inode *, u64, bool); +void f2fs_truncate(struct inode *); +int f2fs_getattr(struct vfsmount *, struct dentry *, struct kstat *); +int f2fs_setattr(struct dentry *, struct iattr *); +int truncate_hole(struct inode *, pgoff_t, pgoff_t); +int truncate_data_blocks_range(struct dnode_of_data *, int); +long f2fs_ioctl(struct file *, unsigned int, unsigned long); +long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); + +/* + * inode.c + */ +void f2fs_set_inode_flags(struct inode *); +struct inode *f2fs_iget(struct super_block *, unsigned long); +int try_to_free_nats(struct f2fs_sb_info *, int); +void update_inode(struct inode *, struct page *); +void update_inode_page(struct inode *); +int f2fs_write_inode(struct inode *, struct writeback_control *); +void f2fs_evict_inode(struct inode *); +void handle_failed_inode(struct inode *); + +/* + * namei.c + */ +struct dentry *f2fs_get_parent(struct dentry *child); + +/* + * dir.c + */ +extern unsigned char f2fs_filetype_table[F2FS_FT_MAX]; +void set_de_type(struct f2fs_dir_entry *, umode_t); +struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *, + struct f2fs_dentry_ptr *); +bool f2fs_fill_dentries(struct file *, void *, filldir_t, + struct f2fs_dentry_ptr *, unsigned int, unsigned int); +void do_make_empty_dir(struct inode *, struct inode *, + struct f2fs_dentry_ptr *); +struct page *init_inode_metadata(struct inode *, struct inode *, + const struct qstr *, struct page *); +void update_parent_metadata(struct inode *, struct inode *, unsigned int); +int room_for_filename(const void *, int, int); +void f2fs_drop_nlink(struct inode *, struct inode *, struct page *); +struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, + struct page **); +struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); +ino_t f2fs_inode_by_name(struct inode *, struct qstr *); +void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, + struct page *, struct inode *); +int update_dent_inode(struct inode *, const struct qstr *); +void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *, + const struct qstr *, f2fs_hash_t , unsigned int); +int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t, + umode_t); +void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *, + struct inode *); +int f2fs_do_tmpfile(struct inode *, struct inode *); +int f2fs_make_empty(struct inode *, struct inode *); +bool f2fs_empty_dir(struct inode *); + +static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) +{ + return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name, + inode, inode->i_ino, inode->i_mode); +} + +/* + * super.c + */ +int f2fs_sync_fs(struct super_block *, int); +extern __printf(3, 4) +void f2fs_msg(struct super_block *, const char *, const char *, ...); + +/* + * hash.c + */ +f2fs_hash_t f2fs_dentry_hash(const struct qstr *); + +/* + * node.c + */ +struct dnode_of_data; +struct node_info; + +bool available_free_memory(struct f2fs_sb_info *, int); +bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); +bool has_fsynced_inode(struct f2fs_sb_info *, nid_t); +bool need_inode_block_update(struct f2fs_sb_info *, nid_t); +void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); +int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); +int truncate_inode_blocks(struct inode *, pgoff_t); +int truncate_xattr_node(struct inode *, struct page *); +int wait_on_node_pages_writeback(struct f2fs_sb_info *, nid_t); +void remove_inode_page(struct inode *); +struct page *new_inode_page(struct inode *); +struct page *new_node_page(struct dnode_of_data *, unsigned int, struct page *); +void ra_node_page(struct f2fs_sb_info *, nid_t); +struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); +struct page *get_node_page_ra(struct page *, int); +void sync_inode_page(struct dnode_of_data *); +int sync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); +bool alloc_nid(struct f2fs_sb_info *, nid_t *); +void alloc_nid_done(struct f2fs_sb_info *, nid_t); +void alloc_nid_failed(struct f2fs_sb_info *, nid_t); +void recover_inline_xattr(struct inode *, struct page *); +void recover_xattr_data(struct inode *, struct page *, block_t); +int recover_inode_page(struct f2fs_sb_info *, struct page *); +int restore_node_summary(struct f2fs_sb_info *, unsigned int, + struct f2fs_summary_block *); +void flush_nat_entries(struct f2fs_sb_info *); +int build_node_manager(struct f2fs_sb_info *); +void destroy_node_manager(struct f2fs_sb_info *); +int __init create_node_manager_caches(void); +void destroy_node_manager_caches(void); + +/* + * segment.c + */ +void register_inmem_page(struct inode *, struct page *); +void commit_inmem_pages(struct inode *, bool); +void f2fs_balance_fs(struct f2fs_sb_info *); +void f2fs_balance_fs_bg(struct f2fs_sb_info *); +int f2fs_issue_flush(struct f2fs_sb_info *); +int create_flush_cmd_control(struct f2fs_sb_info *); +void destroy_flush_cmd_control(struct f2fs_sb_info *); +void invalidate_blocks(struct f2fs_sb_info *, block_t); +void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); +void clear_prefree_segments(struct f2fs_sb_info *); +void release_discard_addrs(struct f2fs_sb_info *); +void discard_next_dnode(struct f2fs_sb_info *, block_t); +int npages_for_summary_flush(struct f2fs_sb_info *, bool); +void allocate_new_segments(struct f2fs_sb_info *); +int f2fs_trim_fs(struct f2fs_sb_info *, struct fstrim_range *); +struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); +void write_meta_page(struct f2fs_sb_info *, struct page *); +void write_node_page(struct f2fs_sb_info *, struct page *, + unsigned int, struct f2fs_io_info *); +void write_data_page(struct page *, struct dnode_of_data *, + struct f2fs_io_info *); +void rewrite_data_page(struct page *, struct f2fs_io_info *); +void recover_data_page(struct f2fs_sb_info *, struct page *, + struct f2fs_summary *, block_t, block_t); +void allocate_data_block(struct f2fs_sb_info *, struct page *, + block_t, block_t *, struct f2fs_summary *, int); +void f2fs_wait_on_page_writeback(struct page *, enum page_type); +void write_data_summaries(struct f2fs_sb_info *, block_t); +void write_node_summaries(struct f2fs_sb_info *, block_t); +int lookup_journal_in_cursum(struct f2fs_summary_block *, + int, unsigned int, int); +void flush_sit_entries(struct f2fs_sb_info *, struct cp_control *); +int build_segment_manager(struct f2fs_sb_info *); +void destroy_segment_manager(struct f2fs_sb_info *); +int __init create_segment_manager_caches(void); +void destroy_segment_manager_caches(void); + +/* + * checkpoint.c + */ +struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); +struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); +int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int); +void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t); +long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); +void add_dirty_inode(struct f2fs_sb_info *, nid_t, int type); +void remove_dirty_inode(struct f2fs_sb_info *, nid_t, int type); +void release_dirty_inode(struct f2fs_sb_info *); +bool exist_written_data(struct f2fs_sb_info *, nid_t, int); +int acquire_orphan_inode(struct f2fs_sb_info *); +void release_orphan_inode(struct f2fs_sb_info *); +void add_orphan_inode(struct f2fs_sb_info *, nid_t); +void remove_orphan_inode(struct f2fs_sb_info *, nid_t); +void recover_orphan_inodes(struct f2fs_sb_info *); +int get_valid_checkpoint(struct f2fs_sb_info *); +void update_dirty_page(struct inode *, struct page *); +void add_dirty_dir_inode(struct inode *); +void remove_dirty_dir_inode(struct inode *); +void sync_dirty_dir_inodes(struct f2fs_sb_info *); +void write_checkpoint(struct f2fs_sb_info *, struct cp_control *); +void init_ino_entry_info(struct f2fs_sb_info *); +int __init create_checkpoint_caches(void); +void destroy_checkpoint_caches(void); + +/* + * data.c + */ +void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); +int f2fs_submit_page_bio(struct f2fs_sb_info *, struct page *, + struct f2fs_io_info *); +void f2fs_submit_page_mbio(struct f2fs_sb_info *, struct page *, + struct f2fs_io_info *); +void set_data_blkaddr(struct dnode_of_data *); +int reserve_new_block(struct dnode_of_data *); +int f2fs_reserve_block(struct dnode_of_data *, pgoff_t); +void f2fs_shrink_extent_tree(struct f2fs_sb_info *, int); +void f2fs_destroy_extent_tree(struct inode *); +void f2fs_init_extent_cache(struct inode *, struct f2fs_extent *); +void f2fs_update_extent_cache(struct dnode_of_data *); +void f2fs_preserve_extent_tree(struct inode *); +struct page *find_data_page(struct inode *, pgoff_t, bool); +struct page *get_lock_data_page(struct inode *, pgoff_t); +struct page *get_new_data_page(struct inode *, struct page *, pgoff_t, bool); +int do_write_data_page(struct page *, struct f2fs_io_info *); +int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *, u64, u64); +void init_extent_cache_info(struct f2fs_sb_info *); +int __init create_extent_cache(void); +void destroy_extent_cache(void); +void f2fs_invalidate_page(struct page *, unsigned long); +int f2fs_release_page(struct page *, gfp_t); + +/* + * gc.c + */ +int start_gc_thread(struct f2fs_sb_info *); +void stop_gc_thread(struct f2fs_sb_info *); +block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *); +int f2fs_gc(struct f2fs_sb_info *); +void build_gc_manager(struct f2fs_sb_info *); + +/* + * recovery.c + */ +int recover_fsync_data(struct f2fs_sb_info *); +bool space_for_roll_forward(struct f2fs_sb_info *); + +/* + * debug.c + */ +#ifdef CONFIG_F2FS_STAT_FS +struct f2fs_stat_info { + struct list_head stat_list; + struct f2fs_sb_info *sbi; + int all_area_segs, sit_area_segs, nat_area_segs, ssa_area_segs; + int main_area_segs, main_area_sections, main_area_zones; + int hit_ext, total_ext, ext_tree, ext_node; + int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta; + int nats, dirty_nats, sits, dirty_sits, fnids; + int total_count, utilization; + int bg_gc, inline_inode, inline_dir, inmem_pages, wb_pages; + unsigned int valid_count, valid_node_count, valid_inode_count; + unsigned int bimodal, avg_vblocks; + int util_free, util_valid, util_invalid; + int rsvd_segs, overp_segs; + int dirty_count, node_pages, meta_pages; + int prefree_count, call_count, cp_count; + int tot_segs, node_segs, data_segs, free_segs, free_secs; + int bg_node_segs, bg_data_segs; + int tot_blks, data_blks, node_blks; + int bg_data_blks, bg_node_blks; + int curseg[NR_CURSEG_TYPE]; + int cursec[NR_CURSEG_TYPE]; + int curzone[NR_CURSEG_TYPE]; + + unsigned int segment_count[2]; + unsigned int block_count[2]; + unsigned int inplace_count; + unsigned base_mem, cache_mem, page_mem; +}; + +static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) +{ + return (struct f2fs_stat_info *)sbi->stat_info; +} + +#define stat_inc_cp_count(si) ((si)->cp_count++) +#define stat_inc_call_count(si) ((si)->call_count++) +#define stat_inc_bggc_count(sbi) ((sbi)->bg_gc++) +#define stat_inc_dirty_dir(sbi) ((sbi)->n_dirty_dirs++) +#define stat_dec_dirty_dir(sbi) ((sbi)->n_dirty_dirs--) +#define stat_inc_total_hit(sb) ((F2FS_SB(sb))->total_hit_ext++) +#define stat_inc_read_hit(sb) ((F2FS_SB(sb))->read_hit_ext++) +#define stat_inc_inline_inode(inode) \ + do { \ + if (f2fs_has_inline_data(inode)) \ + (atomic_inc(&F2FS_I_SB(inode)->inline_inode)); \ + } while (0) +#define stat_dec_inline_inode(inode) \ + do { \ + if (f2fs_has_inline_data(inode)) \ + (atomic_dec(&F2FS_I_SB(inode)->inline_inode)); \ + } while (0) +#define stat_inc_inline_dir(inode) \ + do { \ + if (f2fs_has_inline_dentry(inode)) \ + (atomic_inc(&F2FS_I_SB(inode)->inline_dir)); \ + } while (0) +#define stat_dec_inline_dir(inode) \ + do { \ + if (f2fs_has_inline_dentry(inode)) \ + (atomic_dec(&F2FS_I_SB(inode)->inline_dir)); \ + } while (0) +#define stat_inc_seg_type(sbi, curseg) \ + ((sbi)->segment_count[(curseg)->alloc_type]++) +#define stat_inc_block_count(sbi, curseg) \ + ((sbi)->block_count[(curseg)->alloc_type]++) +#define stat_inc_inplace_blocks(sbi) \ + (atomic_inc(&(sbi)->inplace_count)) +#define stat_inc_seg_count(sbi, type, gc_type) \ + do { \ + struct f2fs_stat_info *si = F2FS_STAT(sbi); \ + (si)->tot_segs++; \ + if (type == SUM_TYPE_DATA) { \ + si->data_segs++; \ + si->bg_data_segs += (gc_type == BG_GC) ? 1 : 0; \ + } else { \ + si->node_segs++; \ + si->bg_node_segs += (gc_type == BG_GC) ? 1 : 0; \ + } \ + } while (0) + +#define stat_inc_tot_blk_count(si, blks) \ + (si->tot_blks += (blks)) + +#define stat_inc_data_blk_count(sbi, blks, gc_type) \ + do { \ + struct f2fs_stat_info *si = F2FS_STAT(sbi); \ + stat_inc_tot_blk_count(si, blks); \ + si->data_blks += (blks); \ + si->bg_data_blks += (gc_type == BG_GC) ? (blks) : 0; \ + } while (0) + +#define stat_inc_node_blk_count(sbi, blks, gc_type) \ + do { \ + struct f2fs_stat_info *si = F2FS_STAT(sbi); \ + stat_inc_tot_blk_count(si, blks); \ + si->node_blks += (blks); \ + si->bg_node_blks += (gc_type == BG_GC) ? (blks) : 0; \ + } while (0) + +int f2fs_build_stats(struct f2fs_sb_info *); +void f2fs_destroy_stats(struct f2fs_sb_info *); +void __init f2fs_create_root_stats(void); +void f2fs_destroy_root_stats(void); +#else +#define stat_inc_cp_count(si) +#define stat_inc_call_count(si) +#define stat_inc_bggc_count(si) +#define stat_inc_dirty_dir(sbi) +#define stat_dec_dirty_dir(sbi) +#define stat_inc_total_hit(sb) +#define stat_inc_read_hit(sb) +#define stat_inc_inline_inode(inode) +#define stat_dec_inline_inode(inode) +#define stat_inc_inline_dir(inode) +#define stat_dec_inline_dir(inode) +#define stat_inc_seg_type(sbi, curseg) +#define stat_inc_block_count(sbi, curseg) +#define stat_inc_inplace_blocks(sbi) +#define stat_inc_seg_count(sbi, type, gc_type) +#define stat_inc_tot_blk_count(si, blks) +#define stat_inc_data_blk_count(sbi, blks, gc_type) +#define stat_inc_node_blk_count(sbi, blks, gc_type) + +static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } +static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } +static inline void __init f2fs_create_root_stats(void) { } +static inline void f2fs_destroy_root_stats(void) { } +#endif + +extern const struct file_operations f2fs_dir_operations; +extern const struct file_operations f2fs_file_operations; +extern const struct inode_operations f2fs_file_inode_operations; +extern const struct address_space_operations f2fs_dblock_aops; +extern const struct address_space_operations f2fs_node_aops; +extern const struct address_space_operations f2fs_meta_aops; +extern const struct inode_operations f2fs_dir_inode_operations; +extern const struct inode_operations f2fs_symlink_inode_operations; +extern const struct inode_operations f2fs_special_inode_operations; +extern struct kmem_cache *inode_entry_slab; + +/* + * inline.c + */ +bool f2fs_may_inline(struct inode *); +void read_inline_data(struct page *, struct page *); +bool truncate_inline_inode(struct page *, u64); +int f2fs_read_inline_data(struct inode *, struct page *); +int f2fs_convert_inline_page(struct dnode_of_data *, struct page *); +int f2fs_convert_inline_inode(struct inode *); +int f2fs_write_inline_data(struct inode *, struct page *); +bool recover_inline_data(struct inode *, struct page *); +struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *, + struct page **); +struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **); +int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *); +int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *, + nid_t, umode_t); +void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *, + struct inode *, struct inode *); +bool f2fs_empty_inline_dir(struct inode *); +int f2fs_read_inline_dir(struct file *, void *, filldir_t); +#endif diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c new file mode 100644 index 000000000000..5d8f0ec77fd7 --- /dev/null +++ b/fs/f2fs/file.c @@ -0,0 +1,1190 @@ +/* + * fs/f2fs/file.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "xattr.h" +#include "acl.h" +#include "trace.h" +#include + +static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct page *page = vmf->page; + struct inode *inode = file_inode(vma->vm_file); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + int err; + + f2fs_balance_fs(sbi); + + vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE); + + f2fs_bug_on(sbi, f2fs_has_inline_data(inode)); + + /* block allocation */ + f2fs_lock_op(sbi); + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = f2fs_reserve_block(&dn, page->index); + if (err) { + f2fs_unlock_op(sbi); + goto out; + } + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); + + file_update_time(vma->vm_file); + lock_page(page); + if (unlikely(page->mapping != inode->i_mapping || + page_offset(page) > i_size_read(inode) || + !PageUptodate(page))) { + unlock_page(page); + err = -EFAULT; + goto out; + } + + /* + * check to see if the page is mapped already (no holes) + */ + if (PageMappedToDisk(page)) + goto mapped; + + /* page is wholly or partially inside EOF */ + if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) { + unsigned offset; + offset = i_size_read(inode) & ~PAGE_CACHE_MASK; + zero_user_segment(page, offset, PAGE_CACHE_SIZE); + } + set_page_dirty(page); + SetPageUptodate(page); + + trace_f2fs_vm_page_mkwrite(page, DATA); +mapped: + /* fill the page */ + f2fs_wait_on_page_writeback(page, DATA); +out: + return block_page_mkwrite_return(err); +} + +static const struct vm_operations_struct f2fs_file_vm_ops = { + .fault = filemap_fault, + .page_mkwrite = f2fs_vm_page_mkwrite, +}; + +static int get_parent_ino(struct inode *inode, nid_t *pino) +{ + struct dentry *dentry; + + inode = igrab(inode); + dentry = d_find_any_alias(inode); + iput(inode); + if (!dentry) + return 0; + + if (update_dent_inode(inode, &dentry->d_name)) { + dput(dentry); + return 0; + } + + *pino = parent_ino(dentry); + dput(dentry); + return 1; +} + +static inline bool need_do_checkpoint(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + bool need_cp = false; + + if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) + need_cp = true; + else if (file_wrong_pino(inode)) + need_cp = true; + else if (!space_for_roll_forward(sbi)) + need_cp = true; + else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) + need_cp = true; + else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi))) + need_cp = true; + else if (test_opt(sbi, FASTBOOT)) + need_cp = true; + else if (sbi->active_logs == 2) + need_cp = true; + + return need_cp; +} + +static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct page *i = find_get_page(NODE_MAPPING(sbi), ino); + bool ret = false; + /* But we need to avoid that there are some inode updates */ + if ((i && PageDirty(i)) || need_inode_block_update(sbi, ino)) + ret = true; + f2fs_put_page(i, 0); + return ret; +} + +static void try_to_fix_pino(struct inode *inode) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + nid_t pino; + + down_write(&fi->i_sem); + fi->xattr_ver = 0; + if (file_wrong_pino(inode) && inode->i_nlink == 1 && + get_parent_ino(inode, &pino)) { + fi->i_pino = pino; + file_got_pino(inode); + up_write(&fi->i_sem); + + mark_inode_dirty_sync(inode); + f2fs_write_inode(inode, NULL); + } else { + up_write(&fi->i_sem); + } +} + +int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) +{ + struct inode *inode = file->f_mapping->host; + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t ino = inode->i_ino; + int ret = 0; + bool need_cp = false; + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = LONG_MAX, + .for_reclaim = 0, + }; + + if (unlikely(f2fs_readonly(inode->i_sb))) + return 0; + + trace_f2fs_sync_file_enter(inode); + + /* if fdatasync is triggered, let's do in-place-update */ + if (get_dirty_pages(inode) <= SM_I(sbi)->min_fsync_blocks) + set_inode_flag(fi, FI_NEED_IPU); + ret = filemap_write_and_wait_range(inode->i_mapping, start, end); + clear_inode_flag(fi, FI_NEED_IPU); + + if (ret) { + trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + return ret; + } + + /* if the inode is dirty, let's recover all the time */ + if (!datasync && is_inode_flag_set(fi, FI_DIRTY_INODE)) { + update_inode_page(inode); + goto go_write; + } + + /* + * if there is no written data, don't waste time to write recovery info. + */ + if (!is_inode_flag_set(fi, FI_APPEND_WRITE) && + !exist_written_data(sbi, ino, APPEND_INO)) { + + /* it may call write_inode just prior to fsync */ + if (need_inode_page_update(sbi, ino)) + goto go_write; + + if (is_inode_flag_set(fi, FI_UPDATE_WRITE) || + exist_written_data(sbi, ino, UPDATE_INO)) + goto flush_out; + goto out; + } +go_write: + /* guarantee free sections for fsync */ + f2fs_balance_fs(sbi); + + /* + * Both of fdatasync() and fsync() are able to be recovered from + * sudden-power-off. + */ + down_read(&fi->i_sem); + need_cp = need_do_checkpoint(inode); + up_read(&fi->i_sem); + + if (need_cp) { + /* all the dirty node pages should be flushed for POR */ + ret = f2fs_sync_fs(inode->i_sb, 1); + + /* + * We've secured consistency through sync_fs. Following pino + * will be used only for fsynced inodes after checkpoint. + */ + try_to_fix_pino(inode); + clear_inode_flag(fi, FI_APPEND_WRITE); + clear_inode_flag(fi, FI_UPDATE_WRITE); + goto out; + } +sync_nodes: + sync_node_pages(sbi, ino, &wbc); + + /* if cp_error was enabled, we should avoid infinite loop */ + if (unlikely(f2fs_cp_error(sbi))) + goto out; + + if (need_inode_block_update(sbi, ino)) { + mark_inode_dirty_sync(inode); + f2fs_write_inode(inode, NULL); + goto sync_nodes; + } + + ret = wait_on_node_pages_writeback(sbi, ino); + if (ret) + goto out; + + /* once recovery info is written, don't need to tack this */ + remove_dirty_inode(sbi, ino, APPEND_INO); + clear_inode_flag(fi, FI_APPEND_WRITE); +flush_out: + remove_dirty_inode(sbi, ino, UPDATE_INO); + clear_inode_flag(fi, FI_UPDATE_WRITE); + ret = f2fs_issue_flush(sbi); +out: + trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + f2fs_trace_ios(NULL, NULL, 1); + return ret; +} + +static pgoff_t __get_first_dirty_index(struct address_space *mapping, + pgoff_t pgofs, int whence) +{ + struct pagevec pvec; + int nr_pages; + + if (whence != SEEK_DATA) + return 0; + + /* find first dirty page index */ + pagevec_init(&pvec, 0); + nr_pages = pagevec_lookup_tag(&pvec, mapping, &pgofs, + PAGECACHE_TAG_DIRTY, 1); + pgofs = nr_pages ? pvec.pages[0]->index : LONG_MAX; + pagevec_release(&pvec); + return pgofs; +} + +static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, + int whence) +{ + switch (whence) { + case SEEK_DATA: + if ((blkaddr == NEW_ADDR && dirty == pgofs) || + (blkaddr != NEW_ADDR && blkaddr != NULL_ADDR)) + return true; + break; + case SEEK_HOLE: + if (blkaddr == NULL_ADDR) + return true; + break; + } + return false; +} + +static inline int unsigned_offsets(struct file *file) +{ + return file->f_mode & FMODE_UNSIGNED_OFFSET; +} + +static loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize) +{ + if (offset < 0 && !unsigned_offsets(file)) + return -EINVAL; + if (offset > maxsize) + return -EINVAL; + + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; + } + return offset; +} + +static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + loff_t maxbytes = inode->i_sb->s_maxbytes; + struct dnode_of_data dn; + pgoff_t pgofs, end_offset, dirty; + loff_t data_ofs = offset; + loff_t isize; + int err = 0; + + mutex_lock(&inode->i_mutex); + + isize = i_size_read(inode); + if (offset >= isize) + goto fail; + + /* handle inline data case */ + if (f2fs_has_inline_data(inode) || f2fs_has_inline_dentry(inode)) { + if (whence == SEEK_HOLE) + data_ofs = isize; + goto found; + } + + pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); + + dirty = __get_first_dirty_index(inode->i_mapping, pgofs, whence); + + for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); + if (err && err != -ENOENT) { + goto fail; + } else if (err == -ENOENT) { + /* direct node does not exists */ + if (whence == SEEK_DATA) { + pgofs = PGOFS_OF_NEXT_DNODE(pgofs, + F2FS_I(inode)); + continue; + } else { + goto found; + } + } + + end_offset = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + + /* find data/hole in dnode block */ + for (; dn.ofs_in_node < end_offset; + dn.ofs_in_node++, pgofs++, + data_ofs = (loff_t)pgofs << PAGE_CACHE_SHIFT) { + block_t blkaddr; + blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); + + if (__found_offset(blkaddr, dirty, pgofs, whence)) { + f2fs_put_dnode(&dn); + goto found; + } + } + f2fs_put_dnode(&dn); + } + + if (whence == SEEK_DATA) + goto fail; +found: + if (whence == SEEK_HOLE && data_ofs > isize) + data_ofs = isize; + mutex_unlock(&inode->i_mutex); + return vfs_setpos(file, data_ofs, maxbytes); +fail: + mutex_unlock(&inode->i_mutex); + return -ENXIO; +} + +static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file->f_mapping->host; + loff_t maxbytes = inode->i_sb->s_maxbytes; + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return generic_file_llseek_size(file, offset, whence, + maxbytes); + case SEEK_DATA: + case SEEK_HOLE: + if (offset < 0) + return -ENXIO; + return f2fs_seek_block(file, offset, whence); + } + + return -EINVAL; +} + +static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file_inode(file); + + /* we don't need to use inline_data strictly */ + if (f2fs_has_inline_data(inode)) { + int err = f2fs_convert_inline_inode(inode); + if (err) + return err; + } + + file_accessed(file); + vma->vm_ops = &f2fs_file_vm_ops; + return 0; +} + +int truncate_data_blocks_range(struct dnode_of_data *dn, int count) +{ + int nr_free = 0, ofs = dn->ofs_in_node; + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_node *raw_node; + __le32 *addr; + + raw_node = F2FS_NODE(dn->node_page); + addr = blkaddr_in_node(raw_node) + ofs; + + for (; count > 0; count--, addr++, dn->ofs_in_node++) { + block_t blkaddr = le32_to_cpu(*addr); + if (blkaddr == NULL_ADDR) + continue; + + dn->data_blkaddr = NULL_ADDR; + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); + invalidate_blocks(sbi, blkaddr); + if (dn->ofs_in_node == 0 && IS_INODE(dn->node_page)) + clear_inode_flag(F2FS_I(dn->inode), + FI_FIRST_BLOCK_WRITTEN); + nr_free++; + } + if (nr_free) { + dec_valid_block_count(sbi, dn->inode, nr_free); + set_page_dirty(dn->node_page); + sync_inode_page(dn); + } + dn->ofs_in_node = ofs; + + trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid, + dn->ofs_in_node, nr_free); + return nr_free; +} + +void truncate_data_blocks(struct dnode_of_data *dn) +{ + truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); +} + +static int truncate_partial_data_page(struct inode *inode, u64 from, + bool force) +{ + unsigned offset = from & (PAGE_CACHE_SIZE - 1); + struct page *page; + + if (!offset && !force) + return 0; + + page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, force); + if (IS_ERR(page)) + return 0; + + lock_page(page); + if (unlikely(!PageUptodate(page) || + page->mapping != inode->i_mapping)) + goto out; + + f2fs_wait_on_page_writeback(page, DATA); + zero_user(page, offset, PAGE_CACHE_SIZE - offset); + if (!force) + set_page_dirty(page); +out: + f2fs_put_page(page, 1); + return 0; +} + +int truncate_blocks(struct inode *inode, u64 from, bool lock) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int blocksize = inode->i_sb->s_blocksize; + struct dnode_of_data dn; + pgoff_t free_from; + int count = 0, err = 0; + struct page *ipage; + bool truncate_page = false; + + trace_f2fs_truncate_blocks_enter(inode, from); + + free_from = (pgoff_t)F2FS_BYTES_TO_BLK(from + blocksize - 1); + + if (lock) + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto out; + } + + if (f2fs_has_inline_data(inode)) { + if (truncate_inline_inode(ipage, from)) + set_page_dirty(ipage); + f2fs_put_page(ipage, 1); + truncate_page = true; + goto out; + } + + set_new_dnode(&dn, inode, ipage, NULL, 0); + err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE); + if (err) { + if (err == -ENOENT) + goto free_next; + goto out; + } + + count = ADDRS_PER_PAGE(dn.node_page, F2FS_I(inode)); + + count -= dn.ofs_in_node; + f2fs_bug_on(sbi, count < 0); + + if (dn.ofs_in_node || IS_INODE(dn.node_page)) { + truncate_data_blocks_range(&dn, count); + free_from += count; + } + + f2fs_put_dnode(&dn); +free_next: + err = truncate_inode_blocks(inode, free_from); +out: + if (lock) + f2fs_unlock_op(sbi); + + /* lastly zero out the first data page */ + if (!err) + err = truncate_partial_data_page(inode, from, truncate_page); + + trace_f2fs_truncate_blocks_exit(inode, err); + return err; +} + +void f2fs_truncate(struct inode *inode) +{ + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return; + + trace_f2fs_truncate(inode); + + /* we should check inline_data size */ + if (f2fs_has_inline_data(inode) && !f2fs_may_inline(inode)) { + if (f2fs_convert_inline_inode(inode)) + return; + } + + if (!truncate_blocks(inode, i_size_read(inode), true)) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + } +} + +int f2fs_getattr(struct vfsmount *mnt, + struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + generic_fillattr(inode, stat); + stat->blocks <<= 3; + return 0; +} + +#ifdef CONFIG_F2FS_FS_POSIX_ACL +static void __setattr_copy(struct inode *inode, const struct iattr *attr) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + unsigned int ia_valid = attr->ia_valid; + + if (ia_valid & ATTR_UID) + inode->i_uid = attr->ia_uid; + if (ia_valid & ATTR_GID) + inode->i_gid = attr->ia_gid; + if (ia_valid & ATTR_ATIME) + inode->i_atime = timespec_trunc(attr->ia_atime, + inode->i_sb->s_time_gran); + if (ia_valid & ATTR_MTIME) + inode->i_mtime = timespec_trunc(attr->ia_mtime, + inode->i_sb->s_time_gran); + if (ia_valid & ATTR_CTIME) + inode->i_ctime = timespec_trunc(attr->ia_ctime, + inode->i_sb->s_time_gran); + if (ia_valid & ATTR_MODE) { + umode_t mode = attr->ia_mode; + + if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) + mode &= ~S_ISGID; + set_acl_inode(fi, mode); + } +} +#else +#define __setattr_copy setattr_copy +#endif + +int f2fs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + struct f2fs_inode_info *fi = F2FS_I(inode); + int err; + + err = inode_change_ok(inode, attr); + if (err) + return err; + + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size != i_size_read(inode)) { + truncate_setsize(inode, attr->ia_size); + f2fs_truncate(inode); + f2fs_balance_fs(F2FS_I_SB(inode)); + } else { + /* + * giving a chance to truncate blocks past EOF which + * are fallocated with FALLOC_FL_KEEP_SIZE. + */ + f2fs_truncate(inode); + } + } + + __setattr_copy(inode, attr); + + if (attr->ia_valid & ATTR_MODE) { + err = f2fs_acl_chmod(inode); + if (err || is_inode_flag_set(fi, FI_ACL_MODE)) { + inode->i_mode = fi->i_acl_mode; + clear_inode_flag(fi, FI_ACL_MODE); + } + } + + mark_inode_dirty(inode); + return err; +} + +const struct inode_operations f2fs_file_inode_operations = { + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, + .get_acl = f2fs_get_acl, +#ifdef CONFIG_F2FS_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +#endif + .fiemap = f2fs_fiemap, +}; + +static void fill_zero(struct inode *inode, pgoff_t index, + loff_t start, loff_t len) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct page *page; + + if (!len) + return; + + f2fs_balance_fs(sbi); + + f2fs_lock_op(sbi); + page = get_new_data_page(inode, NULL, index, false); + f2fs_unlock_op(sbi); + + if (!IS_ERR(page)) { + f2fs_wait_on_page_writeback(page, DATA); + zero_user(page, start, len); + set_page_dirty(page); + f2fs_put_page(page, 1); + } +} + +int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) +{ + pgoff_t index; + int err; + + for (index = pg_start; index < pg_end; index++) { + struct dnode_of_data dn; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, index, LOOKUP_NODE); + if (err) { + if (err == -ENOENT) + continue; + return err; + } + + if (dn.data_blkaddr != NULL_ADDR) + truncate_data_blocks_range(&dn, 1); + f2fs_put_dnode(&dn); + } + return 0; +} + +static int punch_hole(struct inode *inode, loff_t offset, loff_t len) +{ + pgoff_t pg_start, pg_end; + loff_t off_start, off_end; + int ret = 0; + + if (!S_ISREG(inode->i_mode)) + return -EOPNOTSUPP; + + /* skip punching hole beyond i_size */ + if (offset >= inode->i_size) + return ret; + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + + off_start = offset & (PAGE_CACHE_SIZE - 1); + off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + + if (pg_start == pg_end) { + fill_zero(inode, pg_start, off_start, + off_end - off_start); + } else { + if (off_start) + fill_zero(inode, pg_start++, off_start, + PAGE_CACHE_SIZE - off_start); + if (off_end) + fill_zero(inode, pg_end, 0, off_end); + + if (pg_start < pg_end) { + struct address_space *mapping = inode->i_mapping; + loff_t blk_start, blk_end; + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + f2fs_balance_fs(sbi); + + blk_start = pg_start << PAGE_CACHE_SHIFT; + blk_end = pg_end << PAGE_CACHE_SHIFT; + truncate_inode_pages_range(mapping, blk_start, + blk_end - 1); + + f2fs_lock_op(sbi); + ret = truncate_hole(inode, pg_start, pg_end); + f2fs_unlock_op(sbi); + } + } + + return ret; +} + +static int expand_inode_data(struct inode *inode, loff_t offset, + loff_t len, int mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + pgoff_t index, pg_start, pg_end; + loff_t new_size = i_size_read(inode); + loff_t off_start, off_end; + int ret = 0; + + f2fs_balance_fs(sbi); + + ret = inode_newsize_ok(inode, (len + offset)); + if (ret) + return ret; + + if (f2fs_has_inline_data(inode)) { + ret = f2fs_convert_inline_inode(inode); + if (ret) + return ret; + } + + pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; + pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; + + off_start = offset & (PAGE_CACHE_SIZE - 1); + off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); + + f2fs_lock_op(sbi); + + for (index = pg_start; index <= pg_end; index++) { + struct dnode_of_data dn; + + if (index == pg_end && !off_end) + goto noalloc; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + ret = f2fs_reserve_block(&dn, index); + if (ret) + break; +noalloc: + if (pg_start == pg_end) + new_size = offset + len; + else if (index == pg_start && off_start) + new_size = (index + 1) << PAGE_CACHE_SHIFT; + else if (index == pg_end) + new_size = (index << PAGE_CACHE_SHIFT) + off_end; + else + new_size += PAGE_CACHE_SIZE; + } + + if (!(mode & FALLOC_FL_KEEP_SIZE) && + i_size_read(inode) < new_size) { + i_size_write(inode, new_size); + mark_inode_dirty(inode); + update_inode_page(inode); + } + f2fs_unlock_op(sbi); + + return ret; +} + +static long f2fs_fallocate(struct file *file, int mode, + loff_t offset, loff_t len) +{ + struct inode *inode = file_inode(file); + long ret; + + if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) + return -EOPNOTSUPP; + + mutex_lock(&inode->i_mutex); + + if (mode & FALLOC_FL_PUNCH_HOLE) + ret = punch_hole(inode, offset, len); + else + ret = expand_inode_data(inode, offset, len, mode); + + if (!ret) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); + } + + mutex_unlock(&inode->i_mutex); + + trace_f2fs_fallocate(inode, mode, offset, len, ret); + return ret; +} + +static int f2fs_release_file(struct inode *inode, struct file *filp) +{ + /* some remained atomic pages should discarded */ + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, true); + if (f2fs_is_volatile_file(inode)) { + set_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + filemap_fdatawrite(inode->i_mapping); + clear_inode_flag(F2FS_I(inode), FI_DROP_CACHE); + } + return 0; +} + +#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) +#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) + +static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) +{ + if (S_ISDIR(mode)) + return flags; + else if (S_ISREG(mode)) + return flags & F2FS_REG_FLMASK; + else + return flags & F2FS_OTHER_FLMASK; +} + +static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); + unsigned int flags = fi->i_flags & FS_FL_USER_VISIBLE; + return put_user(flags, (int __user *)arg); +} + +static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_inode_info *fi = F2FS_I(inode); + unsigned int flags = fi->i_flags & FS_FL_USER_VISIBLE; + unsigned int oldflags; + int ret; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + if (!inode_owner_or_capable(inode)) { + ret = -EACCES; + goto out; + } + + if (get_user(flags, (int __user *)arg)) { + ret = -EFAULT; + goto out; + } + + flags = f2fs_mask_flags(inode->i_mode, flags); + + mutex_lock(&inode->i_mutex); + + oldflags = fi->i_flags; + + if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { + if (!capable(CAP_LINUX_IMMUTABLE)) { + mutex_unlock(&inode->i_mutex); + ret = -EPERM; + goto out; + } + } + + flags = flags & FS_FL_USER_MODIFIABLE; + flags |= oldflags & ~FS_FL_USER_MODIFIABLE; + fi->i_flags = flags; + mutex_unlock(&inode->i_mutex); + + f2fs_set_inode_flags(inode); + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(inode); +out: + mnt_drop_write_file(filp); + return ret; +} + +static int f2fs_ioc_getversion(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + + return put_user(inode->i_generation, (int __user *)arg); +} + +static int f2fs_ioc_start_atomic_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + f2fs_balance_fs(F2FS_I_SB(inode)); + + if (f2fs_is_atomic_file(inode)) + return 0; + + set_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + + return f2fs_convert_inline_inode(inode); +} + +static int f2fs_ioc_commit_atomic_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + int ret; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (f2fs_is_volatile_file(inode)) + return 0; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, false); + + ret = f2fs_sync_file(filp, 0, LONG_MAX, 0); + mnt_drop_write_file(filp); + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + return ret; +} + +static int f2fs_ioc_start_volatile_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (f2fs_is_volatile_file(inode)) + return 0; + + set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + + return f2fs_convert_inline_inode(inode); +} + +static int f2fs_ioc_release_volatile_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + if (!f2fs_is_volatile_file(inode)) + return 0; + + if (!f2fs_is_first_block_written(inode)) + return truncate_partial_data_page(inode, 0, true); + + punch_hole(inode, 0, F2FS_BLKSIZE); + return 0; +} + +static int f2fs_ioc_abort_volatile_write(struct file *filp) +{ + struct inode *inode = file_inode(filp); + int ret; + + if (!inode_owner_or_capable(inode)) + return -EACCES; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + f2fs_balance_fs(F2FS_I_SB(inode)); + + if (f2fs_is_atomic_file(inode)) { + commit_inmem_pages(inode, false); + clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + } + + if (f2fs_is_volatile_file(inode)) { + clear_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + filemap_fdatawrite(inode->i_mapping); + set_inode_flag(F2FS_I(inode), FI_VOLATILE_FILE); + } + mnt_drop_write_file(filp); + return ret; +} + +static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct super_block *sb = sbi->sb; + __u32 in; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(in, (__u32 __user *)arg)) + return -EFAULT; + + switch (in) { + case F2FS_GOING_DOWN_FULLSYNC: + sb = freeze_bdev(sb->s_bdev); + if (sb && !IS_ERR(sb)) { + f2fs_stop_checkpoint(sbi); + thaw_bdev(sb->s_bdev, sb); + } + break; + case F2FS_GOING_DOWN_METASYNC: + /* do checkpoint only */ + f2fs_sync_fs(sb, 1); + f2fs_stop_checkpoint(sbi); + break; + case F2FS_GOING_DOWN_NOSYNC: + f2fs_stop_checkpoint(sbi); + break; + default: + return -EINVAL; + } + return 0; +} + +static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg) +{ + struct inode *inode = file_inode(filp); + struct super_block *sb = inode->i_sb; + struct request_queue *q = bdev_get_queue(sb->s_bdev); + struct fstrim_range range; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!blk_queue_discard(q)) + return -EOPNOTSUPP; + + if (copy_from_user(&range, (struct fstrim_range __user *)arg, + sizeof(range))) + return -EFAULT; + + range.minlen = max((unsigned int)range.minlen, + q->limits.discard_granularity); + ret = f2fs_trim_fs(F2FS_SB(sb), &range); + if (ret < 0) + return ret; + + if (copy_to_user((struct fstrim_range __user *)arg, &range, + sizeof(range))) + return -EFAULT; + return 0; +} + +long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F2FS_IOC_GETFLAGS: + return f2fs_ioc_getflags(filp, arg); + case F2FS_IOC_SETFLAGS: + return f2fs_ioc_setflags(filp, arg); + case F2FS_IOC_GETVERSION: + return f2fs_ioc_getversion(filp, arg); + case F2FS_IOC_START_ATOMIC_WRITE: + return f2fs_ioc_start_atomic_write(filp); + case F2FS_IOC_COMMIT_ATOMIC_WRITE: + return f2fs_ioc_commit_atomic_write(filp); + case F2FS_IOC_START_VOLATILE_WRITE: + return f2fs_ioc_start_volatile_write(filp); + case F2FS_IOC_RELEASE_VOLATILE_WRITE: + return f2fs_ioc_release_volatile_write(filp); + case F2FS_IOC_ABORT_VOLATILE_WRITE: + return f2fs_ioc_abort_volatile_write(filp); + case F2FS_IOC_SHUTDOWN: + return f2fs_ioc_shutdown(filp, arg); + case FITRIM: + return f2fs_ioc_fitrim(filp, arg); + default: + return -ENOTTY; + } +} + +#ifdef CONFIG_COMPAT +long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F2FS_IOC32_GETFLAGS: + cmd = F2FS_IOC_GETFLAGS; + break; + case F2FS_IOC32_SETFLAGS: + cmd = F2FS_IOC_SETFLAGS; + break; + default: + return -ENOIOCTLCMD; + } + return f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); +} +#endif + +const struct file_operations f2fs_file_operations = { + .llseek = f2fs_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .open = generic_file_open, + .release = f2fs_release_file, + .mmap = f2fs_file_mmap, + .fsync = f2fs_sync_file, + .fallocate = f2fs_fallocate, + .unlocked_ioctl = f2fs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f2fs_compat_ioctl, +#endif + .splice_read = generic_file_splice_read, + .splice_write = generic_file_splice_write, +}; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c new file mode 100644 index 000000000000..733a3651c034 --- /dev/null +++ b/fs/f2fs/gc.c @@ -0,0 +1,746 @@ +/* + * fs/f2fs/gc.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "gc.h" +#include + +static int gc_thread_func(void *data) +{ + struct f2fs_sb_info *sbi = data; + struct f2fs_gc_kthread *gc_th = sbi->gc_thread; + wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; + long wait_ms; + + wait_ms = gc_th->min_sleep_time; + + do { + if (try_to_freeze()) + continue; + else + wait_event_interruptible_timeout(*wq, + kthread_should_stop(), + msecs_to_jiffies(wait_ms)); + if (kthread_should_stop()) + break; + + if (sbi->sb->s_frozen >= SB_FREEZE_WRITE) { + increase_sleep_time(gc_th, &wait_ms); + continue; + } + + /* + * [GC triggering condition] + * 0. GC is not conducted currently. + * 1. There are enough dirty segments. + * 2. IO subsystem is idle by checking the # of writeback pages. + * 3. IO subsystem is idle by checking the # of requests in + * bdev's request list. + * + * Note) We have to avoid triggering GCs frequently. + * Because it is possible that some segments can be + * invalidated soon after by user update or deletion. + * So, I'd like to wait some time to collect dirty segments. + */ + if (!mutex_trylock(&sbi->gc_mutex)) + continue; + + if (!is_idle(sbi)) { + increase_sleep_time(gc_th, &wait_ms); + mutex_unlock(&sbi->gc_mutex); + continue; + } + + if (has_enough_invalid_blocks(sbi)) + decrease_sleep_time(gc_th, &wait_ms); + else + increase_sleep_time(gc_th, &wait_ms); + + stat_inc_bggc_count(sbi); + + /* if return value is not zero, no victim was selected */ + if (f2fs_gc(sbi)) + wait_ms = gc_th->no_gc_sleep_time; + + /* balancing f2fs's metadata periodically */ + f2fs_balance_fs_bg(sbi); + + } while (!kthread_should_stop()); + return 0; +} + +int start_gc_thread(struct f2fs_sb_info *sbi) +{ + struct f2fs_gc_kthread *gc_th; + dev_t dev = sbi->sb->s_bdev->bd_dev; + int err = 0; + + gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); + if (!gc_th) { + err = -ENOMEM; + goto out; + } + + gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME; + gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME; + gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME; + + gc_th->gc_idle = 0; + + sbi->gc_thread = gc_th; + init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); + sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, + "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); + if (IS_ERR(gc_th->f2fs_gc_task)) { + err = PTR_ERR(gc_th->f2fs_gc_task); + kfree(gc_th); + sbi->gc_thread = NULL; + } +out: + return err; +} + +void stop_gc_thread(struct f2fs_sb_info *sbi) +{ + struct f2fs_gc_kthread *gc_th = sbi->gc_thread; + if (!gc_th) + return; + kthread_stop(gc_th->f2fs_gc_task); + kfree(gc_th); + sbi->gc_thread = NULL; +} + +static int select_gc_type(struct f2fs_gc_kthread *gc_th, int gc_type) +{ + int gc_mode = (gc_type == BG_GC) ? GC_CB : GC_GREEDY; + + if (gc_th && gc_th->gc_idle) { + if (gc_th->gc_idle == 1) + gc_mode = GC_CB; + else if (gc_th->gc_idle == 2) + gc_mode = GC_GREEDY; + } + return gc_mode; +} + +static void select_policy(struct f2fs_sb_info *sbi, int gc_type, + int type, struct victim_sel_policy *p) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + + if (p->alloc_mode == SSR) { + p->gc_mode = GC_GREEDY; + p->dirty_segmap = dirty_i->dirty_segmap[type]; + p->max_search = dirty_i->nr_dirty[type]; + p->ofs_unit = 1; + } else { + p->gc_mode = select_gc_type(sbi->gc_thread, gc_type); + p->dirty_segmap = dirty_i->dirty_segmap[DIRTY]; + p->max_search = dirty_i->nr_dirty[DIRTY]; + p->ofs_unit = sbi->segs_per_sec; + } + + if (p->max_search > sbi->max_victim_search) + p->max_search = sbi->max_victim_search; + + p->offset = sbi->last_victim[p->gc_mode]; +} + +static unsigned int get_max_cost(struct f2fs_sb_info *sbi, + struct victim_sel_policy *p) +{ + /* SSR allocates in a segment unit */ + if (p->alloc_mode == SSR) + return 1 << sbi->log_blocks_per_seg; + if (p->gc_mode == GC_GREEDY) + return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; + else if (p->gc_mode == GC_CB) + return UINT_MAX; + else /* No other gc_mode */ + return 0; +} + +static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + unsigned int secno; + + /* + * If the gc_type is FG_GC, we can select victim segments + * selected by background GC before. + * Those segments guarantee they have small valid blocks. + */ + for_each_set_bit(secno, dirty_i->victim_secmap, MAIN_SECS(sbi)) { + if (sec_usage_check(sbi, secno)) + continue; + clear_bit(secno, dirty_i->victim_secmap); + return secno * sbi->segs_per_sec; + } + return NULL_SEGNO; +} + +static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned int secno = GET_SECNO(sbi, segno); + unsigned int start = secno * sbi->segs_per_sec; + unsigned long long mtime = 0; + unsigned int vblocks; + unsigned char age = 0; + unsigned char u; + unsigned int i; + + for (i = 0; i < sbi->segs_per_sec; i++) + mtime += get_seg_entry(sbi, start + i)->mtime; + vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); + + mtime = div_u64(mtime, sbi->segs_per_sec); + vblocks = div_u64(vblocks, sbi->segs_per_sec); + + u = (vblocks * 100) >> sbi->log_blocks_per_seg; + + /* Handle if the system time has changed by the user */ + if (mtime < sit_i->min_mtime) + sit_i->min_mtime = mtime; + if (mtime > sit_i->max_mtime) + sit_i->max_mtime = mtime; + if (sit_i->max_mtime != sit_i->min_mtime) + age = 100 - div64_u64(100 * (mtime - sit_i->min_mtime), + sit_i->max_mtime - sit_i->min_mtime); + + return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); +} + +static inline unsigned int get_gc_cost(struct f2fs_sb_info *sbi, + unsigned int segno, struct victim_sel_policy *p) +{ + if (p->alloc_mode == SSR) + return get_seg_entry(sbi, segno)->ckpt_valid_blocks; + + /* alloc_mode == LFS */ + if (p->gc_mode == GC_GREEDY) + return get_valid_blocks(sbi, segno, sbi->segs_per_sec); + else + return get_cb_cost(sbi, segno); +} + +/* + * This function is called from two paths. + * One is garbage collection and the other is SSR segment selection. + * When it is called during GC, it just gets a victim segment + * and it does not remove it from dirty seglist. + * When it is called from SSR segment selection, it finds a segment + * which has minimum valid blocks and removes it from dirty seglist. + */ +static int get_victim_by_default(struct f2fs_sb_info *sbi, + unsigned int *result, int gc_type, int type, char alloc_mode) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + struct victim_sel_policy p; + unsigned int secno, max_cost; + int nsearched = 0; + + mutex_lock(&dirty_i->seglist_lock); + + p.alloc_mode = alloc_mode; + select_policy(sbi, gc_type, type, &p); + + p.min_segno = NULL_SEGNO; + p.min_cost = max_cost = get_max_cost(sbi, &p); + + if (p.alloc_mode == LFS && gc_type == FG_GC) { + p.min_segno = check_bg_victims(sbi); + if (p.min_segno != NULL_SEGNO) + goto got_it; + } + + while (1) { + unsigned long cost; + unsigned int segno; + + segno = find_next_bit(p.dirty_segmap, MAIN_SEGS(sbi), p.offset); + if (segno >= MAIN_SEGS(sbi)) { + if (sbi->last_victim[p.gc_mode]) { + sbi->last_victim[p.gc_mode] = 0; + p.offset = 0; + continue; + } + break; + } + + p.offset = segno + p.ofs_unit; + if (p.ofs_unit > 1) + p.offset -= segno % p.ofs_unit; + + secno = GET_SECNO(sbi, segno); + + if (sec_usage_check(sbi, secno)) + continue; + if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap)) + continue; + + cost = get_gc_cost(sbi, segno, &p); + + if (p.min_cost > cost) { + p.min_segno = segno; + p.min_cost = cost; + } else if (unlikely(cost == max_cost)) { + continue; + } + + if (nsearched++ >= p.max_search) { + sbi->last_victim[p.gc_mode] = segno; + break; + } + } + if (p.min_segno != NULL_SEGNO) { +got_it: + if (p.alloc_mode == LFS) { + secno = GET_SECNO(sbi, p.min_segno); + if (gc_type == FG_GC) + sbi->cur_victim_sec = secno; + else + set_bit(secno, dirty_i->victim_secmap); + } + *result = (p.min_segno / p.ofs_unit) * p.ofs_unit; + + trace_f2fs_get_victim(sbi->sb, type, gc_type, &p, + sbi->cur_victim_sec, + prefree_segments(sbi), free_segments(sbi)); + } + mutex_unlock(&dirty_i->seglist_lock); + + return (p.min_segno == NULL_SEGNO) ? 0 : 1; +} + +static const struct victim_selection default_v_ops = { + .get_victim = get_victim_by_default, +}; + +static struct inode *find_gc_inode(struct gc_inode_list *gc_list, nid_t ino) +{ + struct inode_entry *ie; + + ie = radix_tree_lookup(&gc_list->iroot, ino); + if (ie) + return ie->inode; + return NULL; +} + +static void add_gc_inode(struct gc_inode_list *gc_list, struct inode *inode) +{ + struct inode_entry *new_ie; + + if (inode == find_gc_inode(gc_list, inode->i_ino)) { + iput(inode); + return; + } + new_ie = f2fs_kmem_cache_alloc(inode_entry_slab, GFP_NOFS); + new_ie->inode = inode; + + f2fs_radix_tree_insert(&gc_list->iroot, inode->i_ino, new_ie); + list_add_tail(&new_ie->list, &gc_list->ilist); +} + +static void put_gc_inode(struct gc_inode_list *gc_list) +{ + struct inode_entry *ie, *next_ie; + list_for_each_entry_safe(ie, next_ie, &gc_list->ilist, list) { + radix_tree_delete(&gc_list->iroot, ie->inode->i_ino); + iput(ie->inode); + list_del(&ie->list); + kmem_cache_free(inode_entry_slab, ie); + } +} + +static int check_valid_map(struct f2fs_sb_info *sbi, + unsigned int segno, int offset) +{ + struct sit_info *sit_i = SIT_I(sbi); + struct seg_entry *sentry; + int ret; + + mutex_lock(&sit_i->sentry_lock); + sentry = get_seg_entry(sbi, segno); + ret = f2fs_test_bit(offset, sentry->cur_valid_map); + mutex_unlock(&sit_i->sentry_lock); + return ret; +} + +/* + * This function compares node address got in summary with that in NAT. + * On validity, copy that node with cold status, otherwise (invalid node) + * ignore that. + */ +static void gc_node_segment(struct f2fs_sb_info *sbi, + struct f2fs_summary *sum, unsigned int segno, int gc_type) +{ + bool initial = true; + struct f2fs_summary *entry; + int off; + +next_step: + entry = sum; + + for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { + nid_t nid = le32_to_cpu(entry->nid); + struct page *node_page; + + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; + + if (check_valid_map(sbi, segno, off) == 0) + continue; + + if (initial) { + ra_node_page(sbi, nid); + continue; + } + node_page = get_node_page(sbi, nid); + if (IS_ERR(node_page)) + continue; + + /* block may become invalid during get_node_page */ + if (check_valid_map(sbi, segno, off) == 0) { + f2fs_put_page(node_page, 1); + continue; + } + + /* set page dirty and write it */ + if (gc_type == FG_GC) { + f2fs_wait_on_page_writeback(node_page, NODE); + set_page_dirty(node_page); + } else { + if (!PageWriteback(node_page)) + set_page_dirty(node_page); + } + f2fs_put_page(node_page, 1); + stat_inc_node_blk_count(sbi, 1, gc_type); + } + + if (initial) { + initial = false; + goto next_step; + } + + if (gc_type == FG_GC) { + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = LONG_MAX, + .for_reclaim = 0, + }; + sync_node_pages(sbi, 0, &wbc); + + /* + * In the case of FG_GC, it'd be better to reclaim this victim + * completely. + */ + if (get_valid_blocks(sbi, segno, 1) != 0) + goto next_step; + } +} + +/* + * Calculate start block index indicating the given node offset. + * Be careful, caller should give this node offset only indicating direct node + * blocks. If any node offsets, which point the other types of node blocks such + * as indirect or double indirect node blocks, are given, it must be a caller's + * bug. + */ +block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) +{ + unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4; + unsigned int bidx; + + if (node_ofs == 0) + return 0; + + if (node_ofs <= 2) { + bidx = node_ofs - 1; + } else if (node_ofs <= indirect_blks) { + int dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1); + bidx = node_ofs - 2 - dec; + } else { + int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); + bidx = node_ofs - 5 - dec; + } + return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi); +} + +static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, + struct node_info *dni, block_t blkaddr, unsigned int *nofs) +{ + struct page *node_page; + nid_t nid; + unsigned int ofs_in_node; + block_t source_blkaddr; + + nid = le32_to_cpu(sum->nid); + ofs_in_node = le16_to_cpu(sum->ofs_in_node); + + node_page = get_node_page(sbi, nid); + if (IS_ERR(node_page)) + return 0; + + get_node_info(sbi, nid, dni); + + if (sum->version != dni->version) { + f2fs_put_page(node_page, 1); + return 0; + } + + *nofs = ofs_of_node(node_page); + source_blkaddr = datablock_addr(node_page, ofs_in_node); + f2fs_put_page(node_page, 1); + + if (source_blkaddr != blkaddr) + return 0; + return 1; +} + +static void move_data_page(struct inode *inode, struct page *page, int gc_type) +{ + struct f2fs_io_info fio = { + .type = DATA, + .rw = WRITE_SYNC, + }; + + if (gc_type == BG_GC) { + if (PageWriteback(page)) + goto out; + set_page_dirty(page); + set_cold_data(page); + } else { + f2fs_wait_on_page_writeback(page, DATA); + + if (clear_page_dirty_for_io(page)) + inode_dec_dirty_pages(inode); + set_cold_data(page); + do_write_data_page(page, &fio); + clear_cold_data(page); + } +out: + f2fs_put_page(page, 1); +} + +/* + * This function tries to get parent node of victim data block, and identifies + * data block validity. If the block is valid, copy that with cold status and + * modify parent node. + * If the parent node is not valid or the data block address is different, + * the victim data block is ignored. + */ +static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, + struct gc_inode_list *gc_list, unsigned int segno, int gc_type) +{ + struct super_block *sb = sbi->sb; + struct f2fs_summary *entry; + block_t start_addr; + int off; + int phase = 0; + + start_addr = START_BLOCK(sbi, segno); + +next_step: + entry = sum; + + for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { + struct page *data_page; + struct inode *inode; + struct node_info dni; /* dnode info for the data */ + unsigned int ofs_in_node, nofs; + block_t start_bidx; + + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; + + if (check_valid_map(sbi, segno, off) == 0) + continue; + + if (phase == 0) { + ra_node_page(sbi, le32_to_cpu(entry->nid)); + continue; + } + + /* Get an inode by ino with checking validity */ + if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) + continue; + + if (phase == 1) { + ra_node_page(sbi, dni.ino); + continue; + } + + ofs_in_node = le16_to_cpu(entry->ofs_in_node); + + if (phase == 2) { + inode = f2fs_iget(sb, dni.ino); + if (IS_ERR(inode) || is_bad_inode(inode)) + continue; + + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + + data_page = find_data_page(inode, + start_bidx + ofs_in_node, false); + if (IS_ERR(data_page)) { + iput(inode); + continue; + } + + f2fs_put_page(data_page, 0); + add_gc_inode(gc_list, inode); + continue; + } + + /* phase 3 */ + inode = find_gc_inode(gc_list, dni.ino); + if (inode) { + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + data_page = get_lock_data_page(inode, + start_bidx + ofs_in_node); + if (IS_ERR(data_page)) + continue; + move_data_page(inode, data_page, gc_type); + stat_inc_data_blk_count(sbi, 1, gc_type); + } + } + + if (++phase < 4) + goto next_step; + + if (gc_type == FG_GC) { + f2fs_submit_merged_bio(sbi, DATA, WRITE); + + /* + * In the case of FG_GC, it'd be better to reclaim this victim + * completely. + */ + if (get_valid_blocks(sbi, segno, 1) != 0) { + phase = 2; + goto next_step; + } + } +} + +static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, + int gc_type) +{ + struct sit_info *sit_i = SIT_I(sbi); + int ret; + + mutex_lock(&sit_i->sentry_lock); + ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, + NO_CHECK_TYPE, LFS); + mutex_unlock(&sit_i->sentry_lock); + return ret; +} + +static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, + struct gc_inode_list *gc_list, int gc_type) +{ + struct page *sum_page; + struct f2fs_summary_block *sum; + struct blk_plug plug; + + /* read segment summary of victim */ + sum_page = get_sum_page(sbi, segno); + + blk_start_plug(&plug); + + sum = page_address(sum_page); + + switch (GET_SUM_TYPE((&sum->footer))) { + case SUM_TYPE_NODE: + gc_node_segment(sbi, sum->entries, segno, gc_type); + break; + case SUM_TYPE_DATA: + gc_data_segment(sbi, sum->entries, gc_list, segno, gc_type); + break; + } + blk_finish_plug(&plug); + + stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)), gc_type); + stat_inc_call_count(sbi->stat_info); + + f2fs_put_page(sum_page, 1); +} + +int f2fs_gc(struct f2fs_sb_info *sbi) +{ + unsigned int segno, i; + int gc_type = BG_GC; + int nfree = 0; + int ret = -1; + struct cp_control cpc; + struct gc_inode_list gc_list = { + .ilist = LIST_HEAD_INIT(gc_list.ilist), + .iroot = RADIX_TREE_INIT(GFP_NOFS), + }; + + cpc.reason = __get_cp_reason(sbi); +gc_more: + if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) + goto stop; + if (unlikely(f2fs_cp_error(sbi))) + goto stop; + + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) { + gc_type = FG_GC; + write_checkpoint(sbi, &cpc); + } + + if (!__get_victim(sbi, &segno, gc_type)) + goto stop; + ret = 0; + + /* readahead multi ssa blocks those have contiguous address */ + if (sbi->segs_per_sec > 1) + ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), sbi->segs_per_sec, + META_SSA); + + for (i = 0; i < sbi->segs_per_sec; i++) + do_garbage_collect(sbi, segno + i, &gc_list, gc_type); + + if (gc_type == FG_GC) { + sbi->cur_victim_sec = NULL_SEGNO; + nfree++; + WARN_ON(get_valid_blocks(sbi, segno, sbi->segs_per_sec)); + } + + if (has_not_enough_free_secs(sbi, nfree)) + goto gc_more; + + if (gc_type == FG_GC) + write_checkpoint(sbi, &cpc); +stop: + mutex_unlock(&sbi->gc_mutex); + + put_gc_inode(&gc_list); + return ret; +} + +void build_gc_manager(struct f2fs_sb_info *sbi) +{ + DIRTY_I(sbi)->v_ops = &default_v_ops; +} diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h new file mode 100644 index 000000000000..9091e0c9ded6 --- /dev/null +++ b/fs/f2fs/gc.h @@ -0,0 +1,110 @@ +/* + * fs/f2fs/gc.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define GC_THREAD_MIN_WB_PAGES 1 /* + * a threshold to determine + * whether IO subsystem is idle + * or not + */ +#define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */ +#define DEF_GC_THREAD_MAX_SLEEP_TIME 60000 +#define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */ +#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ +#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ + +/* Search max. number of dirty segments to select a victim segment */ +#define DEF_MAX_VICTIM_SEARCH 4096 /* covers 8GB */ + +struct f2fs_gc_kthread { + struct task_struct *f2fs_gc_task; + wait_queue_head_t gc_wait_queue_head; + + /* for gc sleep time */ + unsigned int min_sleep_time; + unsigned int max_sleep_time; + unsigned int no_gc_sleep_time; + + /* for changing gc mode */ + unsigned int gc_idle; +}; + +struct gc_inode_list { + struct list_head ilist; + struct radix_tree_root iroot; +}; + +/* + * inline functions + */ +static inline block_t free_user_blocks(struct f2fs_sb_info *sbi) +{ + if (free_segments(sbi) < overprovision_segments(sbi)) + return 0; + else + return (free_segments(sbi) - overprovision_segments(sbi)) + << sbi->log_blocks_per_seg; +} + +static inline block_t limit_invalid_user_blocks(struct f2fs_sb_info *sbi) +{ + return (long)(sbi->user_block_count * LIMIT_INVALID_BLOCK) / 100; +} + +static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) +{ + block_t reclaimable_user_blocks = sbi->user_block_count - + written_block_count(sbi); + return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100; +} + +static inline void increase_sleep_time(struct f2fs_gc_kthread *gc_th, + long *wait) +{ + if (*wait == gc_th->no_gc_sleep_time) + return; + + *wait += gc_th->min_sleep_time; + if (*wait > gc_th->max_sleep_time) + *wait = gc_th->max_sleep_time; +} + +static inline void decrease_sleep_time(struct f2fs_gc_kthread *gc_th, + long *wait) +{ + if (*wait == gc_th->no_gc_sleep_time) + *wait = gc_th->max_sleep_time; + + *wait -= gc_th->min_sleep_time; + if (*wait <= gc_th->min_sleep_time) + *wait = gc_th->min_sleep_time; +} + +static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) +{ + block_t invalid_user_blocks = sbi->user_block_count - + written_block_count(sbi); + /* + * Background GC is triggered with the following conditions. + * 1. There are a number of invalid blocks. + * 2. There is not enough free space. + */ + if (invalid_user_blocks > limit_invalid_user_blocks(sbi) && + free_user_blocks(sbi) < limit_free_user_blocks(sbi)) + return true; + return false; +} + +static inline int is_idle(struct f2fs_sb_info *sbi) +{ + struct block_device *bdev = sbi->sb->s_bdev; + struct request_queue *q = bdev_get_queue(bdev); + struct request_list *rl = &q->rq; + return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); +} diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c new file mode 100644 index 000000000000..a844fcfb9a8d --- /dev/null +++ b/fs/f2fs/hash.c @@ -0,0 +1,104 @@ +/* + * fs/f2fs/hash.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Portions of this code from linux/fs/ext3/hash.c + * + * Copyright (C) 2002 by Theodore Ts'o + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include "f2fs.h" + +/* + * Hashing code copied from ext3 + */ +#define DELTA 0x9E3779B9 + +static void TEA_transform(unsigned int buf[4], unsigned int const in[]) +{ + __u32 sum = 0; + __u32 b0 = buf[0], b1 = buf[1]; + __u32 a = in[0], b = in[1], c = in[2], d = in[3]; + int n = 16; + + do { + sum += DELTA; + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); + } while (--n); + + buf[0] += b0; + buf[1] += b1; +} + +static void str2hashbuf(const unsigned char *msg, size_t len, + unsigned int *buf, int num) +{ + unsigned pad, val; + int i; + + pad = (__u32)len | ((__u32)len << 8); + pad |= pad << 16; + + val = pad; + if (len > num * 4) + len = num * 4; + for (i = 0; i < len; i++) { + if ((i % 4) == 0) + val = pad; + val = msg[i] + (val << 8); + if ((i % 4) == 3) { + *buf++ = val; + val = pad; + num--; + } + } + if (--num >= 0) + *buf++ = val; + while (--num >= 0) + *buf++ = pad; +} + +f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info) +{ + __u32 hash; + f2fs_hash_t f2fs_hash; + const unsigned char *p; + __u32 in[8], buf[4]; + const unsigned char *name = name_info->name; + size_t len = name_info->len; + + if ((len <= 2) && (name[0] == '.') && + (name[1] == '.' || name[1] == '\0')) + return 0; + + /* Initialize the default seed for the hash checksum functions */ + buf[0] = 0x67452301; + buf[1] = 0xefcdab89; + buf[2] = 0x98badcfe; + buf[3] = 0x10325476; + + p = name; + while (1) { + str2hashbuf(p, len, in, 4); + TEA_transform(buf, in); + p += 16; + if (len <= 16) + break; + len -= 16; + } + hash = buf[0]; + f2fs_hash = cpu_to_le32(hash & ~F2FS_HASH_COL_BIT); + return f2fs_hash; +} diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c new file mode 100644 index 000000000000..7885c71e5054 --- /dev/null +++ b/fs/f2fs/inline.c @@ -0,0 +1,536 @@ +/* + * fs/f2fs/inline.c + * Copyright (c) 2013, Intel Corporation + * Authors: Huajun Li + * Haicheng Li + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "f2fs.h" + +bool f2fs_may_inline(struct inode *inode) +{ + if (!test_opt(F2FS_I_SB(inode), INLINE_DATA)) + return false; + + if (f2fs_is_atomic_file(inode)) + return false; + + if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) + return false; + + if (i_size_read(inode) > MAX_INLINE_DATA) + return false; + + return true; +} + +void read_inline_data(struct page *page, struct page *ipage) +{ + void *src_addr, *dst_addr; + + if (PageUptodate(page)) + return; + + f2fs_bug_on(F2FS_P_SB(page), page->index); + + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + + /* Copy the whole inline data block */ + src_addr = inline_data_addr(ipage); + dst_addr = kmap_atomic(page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + flush_dcache_page(page); + kunmap_atomic(dst_addr); + SetPageUptodate(page); +} + +bool truncate_inline_inode(struct page *ipage, u64 from) +{ + void *addr; + + if (from >= MAX_INLINE_DATA) + return false; + + addr = inline_data_addr(ipage); + + f2fs_wait_on_page_writeback(ipage, NODE); + memset(addr + from, 0, MAX_INLINE_DATA - from); + + return true; +} + +int f2fs_read_inline_data(struct inode *inode, struct page *page) +{ + struct page *ipage; + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ipage)) { + unlock_page(page); + return PTR_ERR(ipage); + } + + if (!f2fs_has_inline_data(inode)) { + f2fs_put_page(ipage, 1); + return -EAGAIN; + } + + if (page->index) + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + else + read_inline_data(page, ipage); + + SetPageUptodate(page); + f2fs_put_page(ipage, 1); + unlock_page(page); + return 0; +} + +int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page) +{ + void *src_addr, *dst_addr; + struct f2fs_io_info fio = { + .type = DATA, + .rw = WRITE_SYNC | REQ_PRIO, + }; + int dirty, err; + + f2fs_bug_on(F2FS_I_SB(dn->inode), page->index); + + if (!f2fs_exist_data(dn->inode)) + goto clear_out; + + err = f2fs_reserve_block(dn, 0); + if (err) + return err; + + f2fs_wait_on_page_writeback(page, DATA); + + if (PageUptodate(page)) + goto no_update; + + zero_user_segment(page, MAX_INLINE_DATA, PAGE_CACHE_SIZE); + + /* Copy the whole inline data block */ + src_addr = inline_data_addr(dn->inode_page); + dst_addr = kmap_atomic(page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + flush_dcache_page(page); + kunmap_atomic(dst_addr); + SetPageUptodate(page); +no_update: + /* clear dirty state */ + dirty = clear_page_dirty_for_io(page); + + /* write data page to try to make data consistent */ + set_page_writeback(page); + fio.blk_addr = dn->data_blkaddr; + write_data_page(page, dn, &fio); + set_data_blkaddr(dn); + f2fs_update_extent_cache(dn); + f2fs_wait_on_page_writeback(page, DATA); + if (dirty) + inode_dec_dirty_pages(dn->inode); + + /* this converted inline_data should be recovered. */ + set_inode_flag(F2FS_I(dn->inode), FI_APPEND_WRITE); + + /* clear inline data and flag after data writeback */ + truncate_inline_inode(dn->inode_page, 0); +clear_out: + stat_dec_inline_inode(dn->inode); + f2fs_clear_inline_inode(dn->inode); + sync_inode_page(dn); + f2fs_put_dnode(dn); + return 0; +} + +int f2fs_convert_inline_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct dnode_of_data dn; + struct page *ipage, *page; + int err = 0; + + page = grab_cache_page(inode->i_mapping, 0); + if (!page) + return -ENOMEM; + + f2fs_lock_op(sbi); + + ipage = get_node_page(sbi, inode->i_ino); + if (IS_ERR(ipage)) { + err = PTR_ERR(ipage); + goto out; + } + + set_new_dnode(&dn, inode, ipage, ipage, 0); + + if (f2fs_has_inline_data(inode)) + err = f2fs_convert_inline_page(&dn, page); + + f2fs_put_dnode(&dn); +out: + f2fs_unlock_op(sbi); + + f2fs_put_page(page, 1); + return err; +} + +int f2fs_write_inline_data(struct inode *inode, struct page *page) +{ + void *src_addr, *dst_addr; + struct dnode_of_data dn; + int err; + + set_new_dnode(&dn, inode, NULL, NULL, 0); + err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); + if (err) + return err; + + if (!f2fs_has_inline_data(inode)) { + f2fs_put_dnode(&dn); + return -EAGAIN; + } + + f2fs_bug_on(F2FS_I_SB(inode), page->index); + + f2fs_wait_on_page_writeback(dn.inode_page, NODE); + src_addr = kmap_atomic(page); + dst_addr = inline_data_addr(dn.inode_page); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + kunmap_atomic(src_addr); + + set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + + sync_inode_page(&dn); + f2fs_put_dnode(&dn); + return 0; +} + +bool recover_inline_data(struct inode *inode, struct page *npage) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode *ri = NULL; + void *src_addr, *dst_addr; + struct page *ipage; + + /* + * The inline_data recovery policy is as follows. + * [prev.] [next] of inline_data flag + * o o -> recover inline_data + * o x -> remove inline_data, and then recover data blocks + * x o -> remove inline_data, and then recover inline_data + * x x -> recover data blocks + */ + if (IS_INODE(npage)) + ri = F2FS_INODE(npage); + + if (f2fs_has_inline_data(inode) && + ri && (ri->i_inline & F2FS_INLINE_DATA)) { +process_inline: + ipage = get_node_page(sbi, inode->i_ino); + f2fs_bug_on(sbi, IS_ERR(ipage)); + + f2fs_wait_on_page_writeback(ipage, NODE); + + src_addr = inline_data_addr(npage); + dst_addr = inline_data_addr(ipage); + memcpy(dst_addr, src_addr, MAX_INLINE_DATA); + + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + + update_inode(inode, ipage); + f2fs_put_page(ipage, 1); + return true; + } + + if (f2fs_has_inline_data(inode)) { + ipage = get_node_page(sbi, inode->i_ino); + f2fs_bug_on(sbi, IS_ERR(ipage)); + truncate_inline_inode(ipage, 0); + f2fs_clear_inline_inode(inode); + update_inode(inode, ipage); + f2fs_put_page(ipage, 1); + } else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) { + truncate_blocks(inode, 0, false); + goto process_inline; + } + return false; +} + +struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir, + struct qstr *name, struct page **res_page) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); + struct f2fs_inline_dentry *inline_dentry; + struct f2fs_dir_entry *de; + struct f2fs_dentry_ptr d; + struct page *ipage; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + inline_dentry = inline_data_addr(ipage); + + make_dentry_ptr(&d, (void *)inline_dentry, 2); + de = find_target_dentry(name, NULL, &d); + + unlock_page(ipage); + if (de) + *res_page = ipage; + else + f2fs_put_page(ipage, 0); + + /* + * For the most part, it should be a bug when name_len is zero. + * We stop here for figuring out where the bugs has occurred. + */ + f2fs_bug_on(sbi, d.max < 0); + return de; +} + +struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *dir, + struct page **p) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + struct f2fs_dir_entry *de; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return NULL; + + dentry_blk = inline_data_addr(ipage); + de = &dentry_blk->dentry[1]; + *p = ipage; + unlock_page(ipage); + return de; +} + +int make_empty_inline_dir(struct inode *inode, struct inode *parent, + struct page *ipage) +{ + struct f2fs_inline_dentry *dentry_blk; + struct f2fs_dentry_ptr d; + + dentry_blk = inline_data_addr(ipage); + + make_dentry_ptr(&d, (void *)dentry_blk, 2); + do_make_empty_dir(inode, parent, &d); + + set_page_dirty(ipage); + + /* update i_size to MAX_INLINE_DATA */ + if (i_size_read(inode) < MAX_INLINE_DATA) { + i_size_write(inode, MAX_INLINE_DATA); + set_inode_flag(F2FS_I(inode), FI_UPDATE_DIR); + } + return 0; +} + +static int f2fs_convert_inline_dir(struct inode *dir, struct page *ipage, + struct f2fs_inline_dentry *inline_dentry) +{ + struct page *page; + struct dnode_of_data dn; + struct f2fs_dentry_block *dentry_blk; + int err; + + page = grab_cache_page(dir->i_mapping, 0); + if (!page) + return -ENOMEM; + + set_new_dnode(&dn, dir, ipage, NULL, 0); + err = f2fs_reserve_block(&dn, 0); + if (err) + goto out; + + f2fs_wait_on_page_writeback(page, DATA); + zero_user_segment(page, 0, PAGE_CACHE_SIZE); + + dentry_blk = kmap_atomic(page); + + /* copy data from inline dentry block to new dentry block */ + memcpy(dentry_blk->dentry_bitmap, inline_dentry->dentry_bitmap, + INLINE_DENTRY_BITMAP_SIZE); + memcpy(dentry_blk->dentry, inline_dentry->dentry, + sizeof(struct f2fs_dir_entry) * NR_INLINE_DENTRY); + memcpy(dentry_blk->filename, inline_dentry->filename, + NR_INLINE_DENTRY * F2FS_SLOT_LEN); + + kunmap_atomic(dentry_blk); + SetPageUptodate(page); + set_page_dirty(page); + + /* clear inline dir and flag after data writeback */ + truncate_inline_inode(ipage, 0); + + stat_dec_inline_dir(dir); + clear_inode_flag(F2FS_I(dir), FI_INLINE_DENTRY); + + if (i_size_read(dir) < PAGE_CACHE_SIZE) { + i_size_write(dir, PAGE_CACHE_SIZE); + set_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } + + sync_inode_page(&dn); +out: + f2fs_put_page(page, 1); + return err; +} + +int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name, + struct inode *inode, nid_t ino, umode_t mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos; + f2fs_hash_t name_hash; + size_t namelen = name->len; + struct f2fs_inline_dentry *dentry_blk = NULL; + struct f2fs_dentry_ptr d; + int slots = GET_DENTRY_SLOTS(namelen); + struct page *page = NULL; + int err = 0; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + dentry_blk = inline_data_addr(ipage); + bit_pos = room_for_filename(&dentry_blk->dentry_bitmap, + slots, NR_INLINE_DENTRY); + if (bit_pos >= NR_INLINE_DENTRY) { + err = f2fs_convert_inline_dir(dir, ipage, dentry_blk); + if (!err) + err = -EAGAIN; + goto out; + } + + if (inode) { + down_write(&F2FS_I(inode)->i_sem); + page = init_inode_metadata(inode, dir, name, ipage); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto fail; + } + } + + f2fs_wait_on_page_writeback(ipage, NODE); + + name_hash = f2fs_dentry_hash(name); + make_dentry_ptr(&d, (void *)dentry_blk, 2); + f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos); + + set_page_dirty(ipage); + + /* we don't need to mark_inode_dirty now */ + if (inode) { + F2FS_I(inode)->i_pino = dir->i_ino; + update_inode(inode, page); + f2fs_put_page(page, 1); + } + + update_parent_metadata(dir, inode, 0); +fail: + if (inode) + up_write(&F2FS_I(inode)->i_sem); + + if (is_inode_flag_set(F2FS_I(dir), FI_UPDATE_DIR)) { + update_inode(dir, ipage); + clear_inode_flag(F2FS_I(dir), FI_UPDATE_DIR); + } +out: + f2fs_put_page(ipage, 1); + return err; +} + +void f2fs_delete_inline_entry(struct f2fs_dir_entry *dentry, struct page *page, + struct inode *dir, struct inode *inode) +{ + struct f2fs_inline_dentry *inline_dentry; + int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); + unsigned int bit_pos; + int i; + + lock_page(page); + f2fs_wait_on_page_writeback(page, NODE); + + inline_dentry = inline_data_addr(page); + bit_pos = dentry - inline_dentry->dentry; + for (i = 0; i < slots; i++) + test_and_clear_bit_le(bit_pos + i, + &inline_dentry->dentry_bitmap); + + set_page_dirty(page); + + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + + if (inode) + f2fs_drop_nlink(dir, inode, page); + + f2fs_put_page(page, 1); +} + +bool f2fs_empty_inline_dir(struct inode *dir) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct page *ipage; + unsigned int bit_pos = 2; + struct f2fs_inline_dentry *dentry_blk; + + ipage = get_node_page(sbi, dir->i_ino); + if (IS_ERR(ipage)) + return false; + + dentry_blk = inline_data_addr(ipage); + bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, + NR_INLINE_DENTRY, + bit_pos); + + f2fs_put_page(ipage, 1); + + if (bit_pos < NR_INLINE_DENTRY) + return false; + + return true; +} + +int f2fs_read_inline_dir(struct file *file, void *dirent, filldir_t filldir) +{ + unsigned long pos = file->f_pos; + unsigned int bit_pos = 0; + struct inode *inode = file_inode(file); + struct f2fs_inline_dentry *inline_dentry = NULL; + struct page *ipage = NULL; + struct f2fs_dentry_ptr d; + + if (pos >= NR_INLINE_DENTRY) + return 0; + + bit_pos = (pos % NR_INLINE_DENTRY); + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + if (IS_ERR(ipage)) + return PTR_ERR(ipage); + + inline_dentry = inline_data_addr(ipage); + + make_dentry_ptr(&d, (void *)inline_dentry, 2); + + if (!f2fs_fill_dentries(file, dirent, filldir, &d, 0, bit_pos)) + file->f_pos = NR_INLINE_DENTRY; + + f2fs_put_page(ipage, 1); + return 0; +} diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c new file mode 100644 index 000000000000..45e67f2bcccc --- /dev/null +++ b/fs/f2fs/inode.c @@ -0,0 +1,384 @@ +/* + * fs/f2fs/inode.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" + +#include + +void f2fs_set_inode_flags(struct inode *inode) +{ + unsigned int flags = F2FS_I(inode)->i_flags; + + inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | + S_NOATIME | S_DIRSYNC); + + if (flags & FS_SYNC_FL) + inode->i_flags |= S_SYNC; + if (flags & FS_APPEND_FL) + inode->i_flags |= S_APPEND; + if (flags & FS_IMMUTABLE_FL) + inode->i_flags |= S_IMMUTABLE; + if (flags & FS_NOATIME_FL) + inode->i_flags |= S_NOATIME; + if (flags & FS_DIRSYNC_FL) + inode->i_flags |= S_DIRSYNC; +} + +static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) +{ + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + if (ri->i_addr[0]) + inode->i_rdev = + old_decode_dev(le32_to_cpu(ri->i_addr[0])); + else + inode->i_rdev = + new_decode_dev(le32_to_cpu(ri->i_addr[1])); + } +} + +static bool __written_first_block(struct f2fs_inode *ri) +{ + block_t addr = le32_to_cpu(ri->i_addr[0]); + + if (addr != NEW_ADDR && addr != NULL_ADDR) + return true; + return false; +} + +static void __set_inode_rdev(struct inode *inode, struct f2fs_inode *ri) +{ + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + if (old_valid_dev(inode->i_rdev)) { + ri->i_addr[0] = + cpu_to_le32(old_encode_dev(inode->i_rdev)); + ri->i_addr[1] = 0; + } else { + ri->i_addr[0] = 0; + ri->i_addr[1] = + cpu_to_le32(new_encode_dev(inode->i_rdev)); + ri->i_addr[2] = 0; + } + } +} + +static void __recover_inline_status(struct inode *inode, struct page *ipage) +{ + void *inline_data = inline_data_addr(ipage); + __le32 *start = inline_data; + __le32 *end = start + MAX_INLINE_DATA / sizeof(__le32); + + while (start < end) { + if (*start++) { + f2fs_wait_on_page_writeback(ipage, NODE); + + set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); + set_raw_inline(F2FS_I(inode), F2FS_INODE(ipage)); + set_page_dirty(ipage); + return; + } + } + return; +} + +static int do_read_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); + struct page *node_page; + struct f2fs_inode *ri; + + /* Check if ino is within scope */ + if (check_nid_range(sbi, inode->i_ino)) { + f2fs_msg(inode->i_sb, KERN_ERR, "bad inode number: %lu", + (unsigned long) inode->i_ino); + WARN_ON(1); + return -EINVAL; + } + + node_page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(node_page)) + return PTR_ERR(node_page); + + ri = F2FS_INODE(node_page); + + inode->i_mode = le16_to_cpu(ri->i_mode); + inode->i_uid = le32_to_cpu(ri->i_uid); + inode->i_gid = le32_to_cpu(ri->i_gid); + set_nlink(inode, le32_to_cpu(ri->i_links)); + inode->i_size = le64_to_cpu(ri->i_size); + inode->i_blocks = le64_to_cpu(ri->i_blocks); + + inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime); + inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime); + inode->i_mtime.tv_sec = le64_to_cpu(ri->i_mtime); + inode->i_atime.tv_nsec = le32_to_cpu(ri->i_atime_nsec); + inode->i_ctime.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); + inode->i_mtime.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); + inode->i_generation = le32_to_cpu(ri->i_generation); + + fi->i_current_depth = le32_to_cpu(ri->i_current_depth); + fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid); + fi->i_flags = le32_to_cpu(ri->i_flags); + fi->flags = 0; + fi->i_advise = ri->i_advise; + fi->i_pino = le32_to_cpu(ri->i_pino); + fi->i_dir_level = ri->i_dir_level; + + f2fs_init_extent_cache(inode, &ri->i_ext); + + get_inline_info(fi, ri); + + /* check data exist */ + if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) + __recover_inline_status(inode, node_page); + + /* get rdev by using inline_info */ + __get_inode_rdev(inode, ri); + + if (__written_first_block(ri)) + set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN); + + f2fs_put_page(node_page, 1); + + stat_inc_inline_inode(inode); + stat_inc_inline_dir(inode); + + return 0; +} + +struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct inode *inode; + int ret = 0; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + + if (!(inode->i_state & I_NEW)) { + trace_f2fs_iget(inode); + return inode; + } + if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) + goto make_now; + + ret = do_read_inode(inode); + if (ret) + goto bad_inode; +make_now: + if (ino == F2FS_NODE_INO(sbi)) { + inode->i_mapping->a_ops = &f2fs_node_aops; + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); + } else if (ino == F2FS_META_INO(sbi)) { + inode->i_mapping->a_ops = &f2fs_meta_aops; + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); + } else if (S_ISREG(inode->i_mode)) { + inode->i_op = &f2fs_file_inode_operations; + inode->i_fop = &f2fs_file_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &f2fs_dir_inode_operations; + inode->i_fop = &f2fs_dir_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &f2fs_symlink_inode_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || + S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { + inode->i_op = &f2fs_special_inode_operations; + init_special_inode(inode, inode->i_mode, inode->i_rdev); + } else { + ret = -EIO; + goto bad_inode; + } + unlock_new_inode(inode); + trace_f2fs_iget(inode); + return inode; + +bad_inode: + iget_failed(inode); + trace_f2fs_iget_exit(inode, ret); + return ERR_PTR(ret); +} + +void update_inode(struct inode *inode, struct page *node_page) +{ + struct f2fs_inode *ri; + + f2fs_wait_on_page_writeback(node_page, NODE); + + ri = F2FS_INODE(node_page); + + ri->i_mode = cpu_to_le16(inode->i_mode); + ri->i_advise = F2FS_I(inode)->i_advise; + ri->i_uid = cpu_to_le32(inode->i_uid); + ri->i_gid = cpu_to_le32(inode->i_gid); + ri->i_links = cpu_to_le32(inode->i_nlink); + ri->i_size = cpu_to_le64(i_size_read(inode)); + ri->i_blocks = cpu_to_le64(inode->i_blocks); + + read_lock(&F2FS_I(inode)->ext_lock); + set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext); + read_unlock(&F2FS_I(inode)->ext_lock); + + set_raw_inline(F2FS_I(inode), ri); + + ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); + ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); + ri->i_mtime = cpu_to_le64(inode->i_mtime.tv_sec); + ri->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); + ri->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + ri->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); + ri->i_current_depth = cpu_to_le32(F2FS_I(inode)->i_current_depth); + ri->i_xattr_nid = cpu_to_le32(F2FS_I(inode)->i_xattr_nid); + ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); + ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); + ri->i_generation = cpu_to_le32(inode->i_generation); + ri->i_dir_level = F2FS_I(inode)->i_dir_level; + + __set_inode_rdev(inode, ri); + set_cold_node(inode, node_page); + set_page_dirty(node_page); + + clear_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); +} + +void update_inode_page(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct page *node_page; +retry: + node_page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(node_page)) { + int err = PTR_ERR(node_page); + if (err == -ENOMEM) { + cond_resched(); + goto retry; + } else if (err != -ENOENT) { + f2fs_stop_checkpoint(sbi); + } + return; + } + update_inode(inode, node_page); + f2fs_put_page(node_page, 1); +} + +int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + if (inode->i_ino == F2FS_NODE_INO(sbi) || + inode->i_ino == F2FS_META_INO(sbi)) + return 0; + + if (!is_inode_flag_set(F2FS_I(inode), FI_DIRTY_INODE)) + return 0; + + /* + * We need to lock here to prevent from producing dirty node pages + * during the urgent cleaning time when runing out of free sections. + */ + f2fs_lock_op(sbi); + update_inode_page(inode); + f2fs_unlock_op(sbi); + + if (wbc) + f2fs_balance_fs(sbi); + + return 0; +} + +/* + * Called at the last iput() if i_nlink is zero + */ +void f2fs_evict_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t xnid = F2FS_I(inode)->i_xattr_nid; + + /* some remained atomic pages should discarded */ + if (f2fs_is_atomic_file(inode)) + commit_inmem_pages(inode, true); + + trace_f2fs_evict_inode(inode); + truncate_inode_pages(&inode->i_data, 0); + + if (inode->i_ino == F2FS_NODE_INO(sbi) || + inode->i_ino == F2FS_META_INO(sbi)) + goto out_clear; + + f2fs_bug_on(sbi, get_dirty_pages(inode)); + remove_dirty_dir_inode(inode); + + if (inode->i_nlink || is_bad_inode(inode)) + goto no_delete; + + set_inode_flag(F2FS_I(inode), FI_NO_ALLOC); + i_size_write(inode, 0); + + if (F2FS_HAS_BLOCKS(inode)) + f2fs_truncate(inode); + + f2fs_lock_op(sbi); + remove_inode_page(inode); + f2fs_unlock_op(sbi); + +no_delete: + stat_dec_inline_dir(inode); + stat_dec_inline_inode(inode); + + /* update extent info in inode */ + if (inode->i_nlink) + f2fs_preserve_extent_tree(inode); + f2fs_destroy_extent_tree(inode); + + invalidate_mapping_pages(NODE_MAPPING(sbi), inode->i_ino, inode->i_ino); + if (xnid) + invalidate_mapping_pages(NODE_MAPPING(sbi), xnid, xnid); + if (is_inode_flag_set(F2FS_I(inode), FI_APPEND_WRITE)) + add_dirty_inode(sbi, inode->i_ino, APPEND_INO); + if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE)) + add_dirty_inode(sbi, inode->i_ino, UPDATE_INO); +out_clear: + end_writeback(inode); +} + +/* caller should call f2fs_lock_op() */ +void handle_failed_inode(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + + clear_nlink(inode); + make_bad_inode(inode); + unlock_new_inode(inode); + + i_size_write(inode, 0); + if (F2FS_HAS_BLOCKS(inode)) + f2fs_truncate(inode); + + remove_inode_page(inode); + + clear_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + clear_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); + alloc_nid_failed(sbi, inode->i_ino); + f2fs_unlock_op(sbi); + + /* iput will drop the inode object */ + iput(inode); +} diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c new file mode 100644 index 000000000000..8d8cdf72a525 --- /dev/null +++ b/fs/f2fs/namei.c @@ -0,0 +1,622 @@ +/* + * fs/f2fs/namei.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "xattr.h" +#include "acl.h" +#include + +static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + nid_t ino; + struct inode *inode; + bool nid_free = false; + int err; + + inode = new_inode(dir->i_sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + f2fs_lock_op(sbi); + if (!alloc_nid(sbi, &ino)) { + f2fs_unlock_op(sbi); + err = -ENOSPC; + goto fail; + } + f2fs_unlock_op(sbi); + + inode_init_owner(inode, dir, mode); + + inode->i_ino = ino; + inode->i_blocks = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_generation = sbi->s_next_generation++; + + err = insert_inode_locked(inode); + if (err) { + err = -EINVAL; + nid_free = true; + goto out; + } + + if (f2fs_may_inline(inode)) + set_inode_flag(F2FS_I(inode), FI_INLINE_DATA); + if (test_opt(sbi, INLINE_DENTRY) && S_ISDIR(inode->i_mode)) + set_inode_flag(F2FS_I(inode), FI_INLINE_DENTRY); + + trace_f2fs_new_inode(inode, 0); + mark_inode_dirty(inode); + return inode; + +out: + clear_nlink(inode); + unlock_new_inode(inode); +fail: + trace_f2fs_new_inode(inode, err); + make_bad_inode(inode); + iput(inode); + if (nid_free) + alloc_nid_failed(sbi, ino); + return ERR_PTR(err); +} + +static int is_multimedia_file(const unsigned char *s, const char *sub) +{ + size_t slen = strlen(s); + size_t sublen = strlen(sub); + + if (sublen > slen) + return 0; + + return !strncasecmp(s + slen - sublen, sub, sublen); +} + +/* + * Set multimedia files as cold files for hot/cold data separation + */ +static inline void set_cold_files(struct f2fs_sb_info *sbi, struct inode *inode, + const unsigned char *name) +{ + int i; + __u8 (*extlist)[8] = sbi->raw_super->extension_list; + + int count = le32_to_cpu(sbi->raw_super->extension_count); + for (i = 0; i < count; i++) { + if (is_multimedia_file(name, extlist[i])) { + file_set_cold(inode); + break; + } + } +} + +static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + nid_t ino = 0; + int err; + + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, mode); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + if (!test_opt(sbi, DISABLE_EXT_IDENTIFY)) + set_cold_files(sbi, inode, dentry->d_name.name); + + inode->i_op = &f2fs_file_inode_operations; + inode->i_fop = &f2fs_file_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + ino = inode->i_ino; + + f2fs_lock_op(sbi); + err = f2fs_add_link(dentry, inode); + if (err) + goto out; + f2fs_unlock_op(sbi); + + alloc_nid_done(sbi, ino); + + stat_inc_inline_inode(inode); + d_instantiate(dentry, inode); + unlock_new_inode(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + return 0; +out: + handle_failed_inode(inode); + return err; +} + +static int f2fs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + struct inode *inode = old_dentry->d_inode; + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + int err; + + f2fs_balance_fs(sbi); + + inode->i_ctime = CURRENT_TIME; + ihold(inode); + + set_inode_flag(F2FS_I(inode), FI_INC_LINK); + f2fs_lock_op(sbi); + err = f2fs_add_link(dentry, inode); + if (err) + goto out; + f2fs_unlock_op(sbi); + + d_instantiate(dentry, inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + return 0; +out: + clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + iput(inode); + f2fs_unlock_op(sbi); + return err; +} + +struct dentry *f2fs_get_parent(struct dentry *child) +{ + struct qstr dotdot = {.len = 2, .name = ".."}; + unsigned long ino = f2fs_inode_by_name(child->d_inode, &dotdot); + if (!ino) + return ERR_PTR(-ENOENT); + return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino)); +} + +static int __recover_dot_dentries(struct inode *dir, nid_t pino) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct qstr dot = {.len = 1, .name = "."}; + struct qstr dotdot = {.len = 2, .name = ".."}; + struct f2fs_dir_entry *de; + struct page *page; + int err = 0; + + f2fs_lock_op(sbi); + + de = f2fs_find_entry(dir, &dot, &page); + if (de) { + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + } else { + err = __f2fs_add_link(dir, &dot, NULL, dir->i_ino, S_IFDIR); + if (err) + goto out; + } + + de = f2fs_find_entry(dir, &dotdot, &page); + if (de) { + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + } else { + err = __f2fs_add_link(dir, &dotdot, NULL, pino, S_IFDIR); + } +out: + if (!err) { + clear_inode_flag(F2FS_I(dir), FI_INLINE_DOTS); + mark_inode_dirty(dir); + } + + f2fs_unlock_op(sbi); + return err; +} + +static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct inode *inode = NULL; + struct f2fs_dir_entry *de; + struct page *page; + + if (dentry->d_name.len > F2FS_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + + de = f2fs_find_entry(dir, &dentry->d_name, &page); + if (de) { + nid_t ino = le32_to_cpu(de->ino); + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + + inode = f2fs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + + if (f2fs_has_inline_dots(inode)) { + int err; + + err = __recover_dot_dentries(inode, dir->i_ino); + if (err) { + iget_failed(inode); + return ERR_PTR(err); + } + } + } + + return d_splice_alias(inode, dentry); +} + +static int f2fs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode = dentry->d_inode; + struct f2fs_dir_entry *de; + struct page *page; + int err = -ENOENT; + + trace_f2fs_unlink_enter(dir, dentry); + f2fs_balance_fs(sbi); + + de = f2fs_find_entry(dir, &dentry->d_name, &page); + if (!de) + goto fail; + + f2fs_lock_op(sbi); + err = acquire_orphan_inode(sbi); + if (err) { + f2fs_unlock_op(sbi); + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); + goto fail; + } + f2fs_delete_entry(de, page, dir, inode); + f2fs_unlock_op(sbi); + + /* In order to evict this inode, we set it dirty */ + mark_inode_dirty(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); +fail: + trace_f2fs_unlink_exit(inode, err); + return err; +} + +static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct page *page; + + page = page_follow_link_light(dentry, nd); + if (IS_ERR(page)) + return page; + + /* this is broken symlink case */ + if (*nd_get_link(nd) == 0) { + kunmap(page); + page_cache_release(page); + return ERR_PTR(-ENOENT); + } + return page; +} + +static int f2fs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + size_t symlen = strlen(symname) + 1; + int err; + + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + inode->i_op = &f2fs_symlink_inode_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + + f2fs_lock_op(sbi); + err = f2fs_add_link(dentry, inode); + if (err) + goto out; + f2fs_unlock_op(sbi); + + err = page_symlink(inode, symname, symlen); + alloc_nid_done(sbi, inode->i_ino); + + d_instantiate(dentry, inode); + unlock_new_inode(inode); + + /* + * Let's flush symlink data in order to avoid broken symlink as much as + * possible. Nevertheless, fsyncing is the best way, but there is no + * way to get a file descriptor in order to flush that. + * + * Note that, it needs to do dir->fsync to make this recoverable. + * If the symlink path is stored into inline_data, there is no + * performance regression. + */ + filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + return err; +out: + handle_failed_inode(inode); + return err; +} + +static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + int err; + + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, S_IFDIR | mode); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + inode->i_op = &f2fs_dir_inode_operations; + inode->i_fop = &f2fs_dir_operations; + inode->i_mapping->a_ops = &f2fs_dblock_aops; + mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO); + + set_inode_flag(F2FS_I(inode), FI_INC_LINK); + f2fs_lock_op(sbi); + err = f2fs_add_link(dentry, inode); + if (err) + goto out_fail; + f2fs_unlock_op(sbi); + + stat_inc_inline_dir(inode); + alloc_nid_done(sbi, inode->i_ino); + + d_instantiate(dentry, inode); + unlock_new_inode(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + return 0; + +out_fail: + clear_inode_flag(F2FS_I(inode), FI_INC_LINK); + handle_failed_inode(inode); + return err; +} + +static int f2fs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + if (f2fs_empty_dir(inode)) + return f2fs_unlink(dir, dentry); + return -ENOTEMPTY; +} + +static int f2fs_mknod(struct inode *dir, struct dentry *dentry, + umode_t mode, dev_t rdev) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dir); + struct inode *inode; + int err = 0; + + if (!new_valid_dev(rdev)) + return -EINVAL; + + f2fs_balance_fs(sbi); + + inode = f2fs_new_inode(dir, mode); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + init_special_inode(inode, inode->i_mode, rdev); + inode->i_op = &f2fs_special_inode_operations; + + f2fs_lock_op(sbi); + err = f2fs_add_link(dentry, inode); + if (err) + goto out; + f2fs_unlock_op(sbi); + + alloc_nid_done(sbi, inode->i_ino); + + d_instantiate(dentry, inode); + unlock_new_inode(inode); + + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + return 0; +out: + handle_failed_inode(inode); + return err; +} + +static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(old_dir); + struct inode *old_inode = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; + struct page *old_dir_page; + struct page *old_page, *new_page; + struct f2fs_dir_entry *old_dir_entry = NULL; + struct f2fs_dir_entry *old_entry; + struct f2fs_dir_entry *new_entry; + int err = -ENOENT; + + f2fs_balance_fs(sbi); + + old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); + if (!old_entry) + goto out; + + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; + old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); + if (!old_dir_entry) + goto out_old; + } + + if (new_inode) { + + err = -ENOTEMPTY; + if (old_dir_entry && !f2fs_empty_dir(new_inode)) + goto out_dir; + + err = -ENOENT; + new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, + &new_page); + if (!new_entry) + goto out_dir; + + f2fs_lock_op(sbi); + + err = acquire_orphan_inode(sbi); + if (err) + goto put_out_dir; + + if (update_dent_inode(old_inode, &new_dentry->d_name)) { + release_orphan_inode(sbi); + goto put_out_dir; + } + + f2fs_set_link(new_dir, new_entry, new_page, old_inode); + + new_inode->i_ctime = CURRENT_TIME; + down_write(&F2FS_I(new_inode)->i_sem); + if (old_dir_entry) + drop_nlink(new_inode); + drop_nlink(new_inode); + up_write(&F2FS_I(new_inode)->i_sem); + + mark_inode_dirty(new_inode); + + if (!new_inode->i_nlink) + add_orphan_inode(sbi, new_inode->i_ino); + else + release_orphan_inode(sbi); + + update_inode_page(old_inode); + update_inode_page(new_inode); + } else { + f2fs_lock_op(sbi); + + err = f2fs_add_link(new_dentry, old_inode); + if (err) { + f2fs_unlock_op(sbi); + goto out_dir; + } + + if (old_dir_entry) { + inc_nlink(new_dir); + update_inode_page(new_dir); + } + } + + down_write(&F2FS_I(old_inode)->i_sem); + file_lost_pino(old_inode); + up_write(&F2FS_I(old_inode)->i_sem); + + old_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty(old_inode); + + f2fs_delete_entry(old_entry, old_page, old_dir, NULL); + + if (old_dir_entry) { + if (old_dir != new_dir) { + f2fs_set_link(old_inode, old_dir_entry, + old_dir_page, new_dir); + update_inode_page(old_inode); + } else { + f2fs_dentry_kunmap(old_inode, old_dir_page); + f2fs_put_page(old_dir_page, 0); + } + drop_nlink(old_dir); + mark_inode_dirty(old_dir); + update_inode_page(old_dir); + } + + f2fs_unlock_op(sbi); + + if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir)) + f2fs_sync_fs(sbi->sb, 1); + return 0; + +put_out_dir: + f2fs_unlock_op(sbi); + f2fs_dentry_kunmap(new_dir, new_page); + f2fs_put_page(new_page, 0); +out_dir: + if (old_dir_entry) { + f2fs_dentry_kunmap(old_inode, old_dir_page); + f2fs_put_page(old_dir_page, 0); + } +out_old: + f2fs_dentry_kunmap(old_dir, old_page); + f2fs_put_page(old_page, 0); +out: + return err; +} + +const struct inode_operations f2fs_dir_inode_operations = { + .create = f2fs_create, + .lookup = f2fs_lookup, + .link = f2fs_link, + .unlink = f2fs_unlink, + .symlink = f2fs_symlink, + .mkdir = f2fs_mkdir, + .rmdir = f2fs_rmdir, + .mknod = f2fs_mknod, + .rename = f2fs_rename, + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, + .get_acl = f2fs_get_acl, +#ifdef CONFIG_F2FS_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +#endif +}; + +const struct inode_operations f2fs_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = f2fs_follow_link, + .put_link = page_put_link, + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, +#ifdef CONFIG_F2FS_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +#endif +}; + +const struct inode_operations f2fs_special_inode_operations = { + .getattr = f2fs_getattr, + .setattr = f2fs_setattr, + .get_acl = f2fs_get_acl, +#ifdef CONFIG_F2FS_FS_XATTR + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = f2fs_listxattr, + .removexattr = generic_removexattr, +#endif +}; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c new file mode 100644 index 000000000000..32f4934c6a2a --- /dev/null +++ b/fs/f2fs/node.c @@ -0,0 +1,2086 @@ +/* + * fs/f2fs/node.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "trace.h" +#include + +#define on_build_free_nids(nmi) mutex_is_locked(&nm_i->build_lock) + +static struct kmem_cache *nat_entry_slab; +static struct kmem_cache *free_nid_slab; +static struct kmem_cache *nat_entry_set_slab; + +bool available_free_memory(struct f2fs_sb_info *sbi, int type) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct sysinfo val; + unsigned long avail_ram; + unsigned long mem_size = 0; + bool res = false; + + si_meminfo(&val); + + /* only uses low memory */ + avail_ram = val.totalram - val.totalhigh; + + /* + * give 25%, 25%, 50%, 50%, 50% memory for each components respectively + */ + if (type == FREE_NIDS) { + mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> + PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); + } else if (type == NAT_ENTRIES) { + mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> + PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); + } else if (type == DIRTY_DENTS) { + if (sbi->sb->s_bdi->dirty_exceeded) + return false; + mem_size = get_pages(sbi, F2FS_DIRTY_DENTS); + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == INO_ENTRIES) { + int i; + + for (i = 0; i <= UPDATE_INO; i++) + mem_size += (sbi->im[i].ino_num * + sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else if (type == EXTENT_CACHE) { + mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) + + atomic_read(&sbi->total_ext_node) * + sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT; + res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1); + } else { + if (sbi->sb->s_bdi->dirty_exceeded) + return false; + } + return res; +} + +static void clear_node_page_dirty(struct page *page) +{ + struct address_space *mapping = page->mapping; + unsigned int long flags; + + if (PageDirty(page)) { + spin_lock_irqsave(&mapping->tree_lock, flags); + radix_tree_tag_clear(&mapping->page_tree, + page_index(page), + PAGECACHE_TAG_DIRTY); + spin_unlock_irqrestore(&mapping->tree_lock, flags); + + clear_page_dirty_for_io(page); + dec_page_count(F2FS_M_SB(mapping), F2FS_DIRTY_NODES); + } + ClearPageUptodate(page); +} + +static struct page *get_current_nat_page(struct f2fs_sb_info *sbi, nid_t nid) +{ + pgoff_t index = current_nat_addr(sbi, nid); + return get_meta_page(sbi, index); +} + +static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct page *src_page; + struct page *dst_page; + pgoff_t src_off; + pgoff_t dst_off; + void *src_addr; + void *dst_addr; + struct f2fs_nm_info *nm_i = NM_I(sbi); + + src_off = current_nat_addr(sbi, nid); + dst_off = next_nat_addr(sbi, src_off); + + /* get current nat block page with lock */ + src_page = get_meta_page(sbi, src_off); + dst_page = grab_meta_page(sbi, dst_off); + f2fs_bug_on(sbi, PageDirty(src_page)); + + src_addr = page_address(src_page); + dst_addr = page_address(dst_page); + memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); + set_page_dirty(dst_page); + f2fs_put_page(src_page, 1); + + set_to_next_nat(nm_i, nid); + + return dst_page; +} + +static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n) +{ + return radix_tree_lookup(&nm_i->nat_root, n); +} + +static unsigned int __gang_lookup_nat_cache(struct f2fs_nm_info *nm_i, + nid_t start, unsigned int nr, struct nat_entry **ep) +{ + return radix_tree_gang_lookup(&nm_i->nat_root, (void **)ep, start, nr); +} + +static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) +{ + list_del(&e->list); + radix_tree_delete(&nm_i->nat_root, nat_get_nid(e)); + nm_i->nat_cnt--; + kmem_cache_free(nat_entry_slab, e); +} + +static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i, + struct nat_entry *ne) +{ + nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid); + struct nat_entry_set *head; + + if (get_nat_flag(ne, IS_DIRTY)) + return; + + head = radix_tree_lookup(&nm_i->nat_set_root, set); + if (!head) { + head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); + + INIT_LIST_HEAD(&head->entry_list); + INIT_LIST_HEAD(&head->set_list); + head->set = set; + head->entry_cnt = 0; + f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head); + } + list_move_tail(&ne->list, &head->entry_list); + nm_i->dirty_nat_cnt++; + head->entry_cnt++; + set_nat_flag(ne, IS_DIRTY, true); +} + +static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i, + struct nat_entry *ne) +{ + nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid); + struct nat_entry_set *head; + + head = radix_tree_lookup(&nm_i->nat_set_root, set); + if (head) { + list_move_tail(&ne->list, &nm_i->nat_entries); + set_nat_flag(ne, IS_DIRTY, false); + head->entry_cnt--; + nm_i->dirty_nat_cnt--; + } +} + +static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i, + nid_t start, unsigned int nr, struct nat_entry_set **ep) +{ + return radix_tree_gang_lookup(&nm_i->nat_set_root, (void **)ep, + start, nr); +} + +bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct nat_entry *e; + bool is_cp = true; + + down_read(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, nid); + if (e && !get_nat_flag(e, IS_CHECKPOINTED)) + is_cp = false; + up_read(&nm_i->nat_tree_lock); + return is_cp; +} + +bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct nat_entry *e; + bool fsynced = false; + + down_read(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, ino); + if (e && get_nat_flag(e, HAS_FSYNCED_INODE)) + fsynced = true; + up_read(&nm_i->nat_tree_lock); + return fsynced; +} + +bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct nat_entry *e; + bool need_update = true; + + down_read(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, ino); + if (e && get_nat_flag(e, HAS_LAST_FSYNC) && + (get_nat_flag(e, IS_CHECKPOINTED) || + get_nat_flag(e, HAS_FSYNCED_INODE))) + need_update = false; + up_read(&nm_i->nat_tree_lock); + return need_update; +} + +static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) +{ + struct nat_entry *new; + + new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC); + f2fs_radix_tree_insert(&nm_i->nat_root, nid, new); + memset(new, 0, sizeof(struct nat_entry)); + nat_set_nid(new, nid); + nat_reset_flag(new); + list_add_tail(&new->list, &nm_i->nat_entries); + nm_i->nat_cnt++; + return new; +} + +static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, + struct f2fs_nat_entry *ne) +{ + struct nat_entry *e; + + down_write(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, nid); + if (!e) { + e = grab_nat_entry(nm_i, nid); + node_info_from_raw_nat(&e->ni, ne); + } + up_write(&nm_i->nat_tree_lock); +} + +static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, + block_t new_blkaddr, bool fsync_done) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct nat_entry *e; + + down_write(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, ni->nid); + if (!e) { + e = grab_nat_entry(nm_i, ni->nid); + copy_node_info(&e->ni, ni); + f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR); + } else if (new_blkaddr == NEW_ADDR) { + /* + * when nid is reallocated, + * previous nat entry can be remained in nat cache. + * So, reinitialize it with new information. + */ + copy_node_info(&e->ni, ni); + f2fs_bug_on(sbi, ni->blk_addr != NULL_ADDR); + } + + /* sanity check */ + f2fs_bug_on(sbi, nat_get_blkaddr(e) != ni->blk_addr); + f2fs_bug_on(sbi, nat_get_blkaddr(e) == NULL_ADDR && + new_blkaddr == NULL_ADDR); + f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR && + new_blkaddr == NEW_ADDR); + f2fs_bug_on(sbi, nat_get_blkaddr(e) != NEW_ADDR && + nat_get_blkaddr(e) != NULL_ADDR && + new_blkaddr == NEW_ADDR); + + /* increment version no as node is removed */ + if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) { + unsigned char version = nat_get_version(e); + nat_set_version(e, inc_node_version(version)); + } + + /* change address */ + nat_set_blkaddr(e, new_blkaddr); + if (new_blkaddr == NEW_ADDR || new_blkaddr == NULL_ADDR) + set_nat_flag(e, IS_CHECKPOINTED, false); + __set_nat_cache_dirty(nm_i, e); + + /* update fsync_mark if its inode nat entry is still alive */ + e = __lookup_nat_cache(nm_i, ni->ino); + if (e) { + if (fsync_done && ni->nid == ni->ino) + set_nat_flag(e, HAS_FSYNCED_INODE, true); + set_nat_flag(e, HAS_LAST_FSYNC, fsync_done); + } + up_write(&nm_i->nat_tree_lock); +} + +int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + if (available_free_memory(sbi, NAT_ENTRIES)) + return 0; + + down_write(&nm_i->nat_tree_lock); + while (nr_shrink && !list_empty(&nm_i->nat_entries)) { + struct nat_entry *ne; + ne = list_first_entry(&nm_i->nat_entries, + struct nat_entry, list); + __del_from_nat_cache(nm_i, ne); + nr_shrink--; + } + up_write(&nm_i->nat_tree_lock); + return nr_shrink; +} + +/* + * This function always returns success + */ +void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + nid_t start_nid = START_NID(nid); + struct f2fs_nat_block *nat_blk; + struct page *page = NULL; + struct f2fs_nat_entry ne; + struct nat_entry *e; + int i; + + ni->nid = nid; + + /* Check nat cache */ + down_read(&nm_i->nat_tree_lock); + e = __lookup_nat_cache(nm_i, nid); + if (e) { + ni->ino = nat_get_ino(e); + ni->blk_addr = nat_get_blkaddr(e); + ni->version = nat_get_version(e); + } + up_read(&nm_i->nat_tree_lock); + if (e) + return; + + memset(&ne, 0, sizeof(struct f2fs_nat_entry)); + + /* Check current segment summary */ + mutex_lock(&curseg->curseg_mutex); + i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); + if (i >= 0) { + ne = nat_in_journal(sum, i); + node_info_from_raw_nat(ni, &ne); + } + mutex_unlock(&curseg->curseg_mutex); + if (i >= 0) + goto cache; + + /* Fill node_info from nat page */ + page = get_current_nat_page(sbi, start_nid); + nat_blk = (struct f2fs_nat_block *)page_address(page); + ne = nat_blk->entries[nid - start_nid]; + node_info_from_raw_nat(ni, &ne); + f2fs_put_page(page, 1); +cache: + /* cache nat entry */ + cache_nat_entry(NM_I(sbi), nid, &ne); +} + +/* + * The maximum depth is four. + * Offset[0] will have raw inode offset. + */ +static int get_node_path(struct f2fs_inode_info *fi, long block, + int offset[4], unsigned int noffset[4]) +{ + const long direct_index = ADDRS_PER_INODE(fi); + const long direct_blks = ADDRS_PER_BLOCK; + const long dptrs_per_blk = NIDS_PER_BLOCK; + const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; + const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK; + int n = 0; + int level = 0; + + noffset[0] = 0; + + if (block < direct_index) { + offset[n] = block; + goto got; + } + block -= direct_index; + if (block < direct_blks) { + offset[n++] = NODE_DIR1_BLOCK; + noffset[n] = 1; + offset[n] = block; + level = 1; + goto got; + } + block -= direct_blks; + if (block < direct_blks) { + offset[n++] = NODE_DIR2_BLOCK; + noffset[n] = 2; + offset[n] = block; + level = 1; + goto got; + } + block -= direct_blks; + if (block < indirect_blks) { + offset[n++] = NODE_IND1_BLOCK; + noffset[n] = 3; + offset[n++] = block / direct_blks; + noffset[n] = 4 + offset[n - 1]; + offset[n] = block % direct_blks; + level = 2; + goto got; + } + block -= indirect_blks; + if (block < indirect_blks) { + offset[n++] = NODE_IND2_BLOCK; + noffset[n] = 4 + dptrs_per_blk; + offset[n++] = block / direct_blks; + noffset[n] = 5 + dptrs_per_blk + offset[n - 1]; + offset[n] = block % direct_blks; + level = 2; + goto got; + } + block -= indirect_blks; + if (block < dindirect_blks) { + offset[n++] = NODE_DIND_BLOCK; + noffset[n] = 5 + (dptrs_per_blk * 2); + offset[n++] = block / indirect_blks; + noffset[n] = 6 + (dptrs_per_blk * 2) + + offset[n - 1] * (dptrs_per_blk + 1); + offset[n++] = (block / direct_blks) % dptrs_per_blk; + noffset[n] = 7 + (dptrs_per_blk * 2) + + offset[n - 2] * (dptrs_per_blk + 1) + + offset[n - 1]; + offset[n] = block % direct_blks; + level = 3; + goto got; + } else { + BUG(); + } +got: + return level; +} + +/* + * Caller should call f2fs_put_dnode(dn). + * Also, it should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op() only if ro is not set RDONLY_NODE. + * In the case of RDONLY_NODE, we don't need to care about mutex. + */ +int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct page *npage[4]; + struct page *parent = NULL; + int offset[4]; + unsigned int noffset[4]; + nid_t nids[4]; + int level, i; + int err = 0; + + level = get_node_path(F2FS_I(dn->inode), index, offset, noffset); + + nids[0] = dn->inode->i_ino; + npage[0] = dn->inode_page; + + if (!npage[0]) { + npage[0] = get_node_page(sbi, nids[0]); + if (IS_ERR(npage[0])) + return PTR_ERR(npage[0]); + } + + /* if inline_data is set, should not report any block indices */ + if (f2fs_has_inline_data(dn->inode) && index) { + err = -ENOENT; + f2fs_put_page(npage[0], 1); + goto release_out; + } + + parent = npage[0]; + if (level != 0) + nids[1] = get_nid(parent, offset[0], true); + dn->inode_page = npage[0]; + dn->inode_page_locked = true; + + /* get indirect or direct nodes */ + for (i = 1; i <= level; i++) { + bool done = false; + + if (!nids[i] && mode == ALLOC_NODE) { + /* alloc new node */ + if (!alloc_nid(sbi, &(nids[i]))) { + err = -ENOSPC; + goto release_pages; + } + + dn->nid = nids[i]; + npage[i] = new_node_page(dn, noffset[i], NULL); + if (IS_ERR(npage[i])) { + alloc_nid_failed(sbi, nids[i]); + err = PTR_ERR(npage[i]); + goto release_pages; + } + + set_nid(parent, offset[i - 1], nids[i], i == 1); + alloc_nid_done(sbi, nids[i]); + done = true; + } else if (mode == LOOKUP_NODE_RA && i == level && level > 1) { + npage[i] = get_node_page_ra(parent, offset[i - 1]); + if (IS_ERR(npage[i])) { + err = PTR_ERR(npage[i]); + goto release_pages; + } + done = true; + } + if (i == 1) { + dn->inode_page_locked = false; + unlock_page(parent); + } else { + f2fs_put_page(parent, 1); + } + + if (!done) { + npage[i] = get_node_page(sbi, nids[i]); + if (IS_ERR(npage[i])) { + err = PTR_ERR(npage[i]); + f2fs_put_page(npage[0], 0); + goto release_out; + } + } + if (i < level) { + parent = npage[i]; + nids[i + 1] = get_nid(parent, offset[i], false); + } + } + dn->nid = nids[level]; + dn->ofs_in_node = offset[level]; + dn->node_page = npage[level]; + dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); + return 0; + +release_pages: + f2fs_put_page(parent, 1); + if (i > 1) + f2fs_put_page(npage[0], 0); +release_out: + dn->inode_page = NULL; + dn->node_page = NULL; + return err; +} + +static void truncate_node(struct dnode_of_data *dn) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct node_info ni; + + get_node_info(sbi, dn->nid, &ni); + if (dn->inode->i_blocks == 0) { + f2fs_bug_on(sbi, ni.blk_addr != NULL_ADDR); + goto invalidate; + } + f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); + + /* Deallocate node address */ + invalidate_blocks(sbi, ni.blk_addr); + dec_valid_node_count(sbi, dn->inode); + set_node_addr(sbi, &ni, NULL_ADDR, false); + + if (dn->nid == dn->inode->i_ino) { + remove_orphan_inode(sbi, dn->nid); + dec_valid_inode_count(sbi); + } else { + sync_inode_page(dn); + } +invalidate: + clear_node_page_dirty(dn->node_page); + set_sbi_flag(sbi, SBI_IS_DIRTY); + + f2fs_put_page(dn->node_page, 1); + + invalidate_mapping_pages(NODE_MAPPING(sbi), + dn->node_page->index, dn->node_page->index); + + dn->node_page = NULL; + trace_f2fs_truncate_node(dn->inode, dn->nid, ni.blk_addr); +} + +static int truncate_dnode(struct dnode_of_data *dn) +{ + struct page *page; + + if (dn->nid == 0) + return 1; + + /* get direct node */ + page = get_node_page(F2FS_I_SB(dn->inode), dn->nid); + if (IS_ERR(page) && PTR_ERR(page) == -ENOENT) + return 1; + else if (IS_ERR(page)) + return PTR_ERR(page); + + /* Make dnode_of_data for parameter */ + dn->node_page = page; + dn->ofs_in_node = 0; + truncate_data_blocks(dn); + truncate_node(dn); + return 1; +} + +static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, + int ofs, int depth) +{ + struct dnode_of_data rdn = *dn; + struct page *page; + struct f2fs_node *rn; + nid_t child_nid; + unsigned int child_nofs; + int freed = 0; + int i, ret; + + if (dn->nid == 0) + return NIDS_PER_BLOCK + 1; + + trace_f2fs_truncate_nodes_enter(dn->inode, dn->nid, dn->data_blkaddr); + + page = get_node_page(F2FS_I_SB(dn->inode), dn->nid); + if (IS_ERR(page)) { + trace_f2fs_truncate_nodes_exit(dn->inode, PTR_ERR(page)); + return PTR_ERR(page); + } + + rn = F2FS_NODE(page); + if (depth < 3) { + for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) { + child_nid = le32_to_cpu(rn->in.nid[i]); + if (child_nid == 0) + continue; + rdn.nid = child_nid; + ret = truncate_dnode(&rdn); + if (ret < 0) + goto out_err; + set_nid(page, i, 0, false); + } + } else { + child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1; + for (i = ofs; i < NIDS_PER_BLOCK; i++) { + child_nid = le32_to_cpu(rn->in.nid[i]); + if (child_nid == 0) { + child_nofs += NIDS_PER_BLOCK + 1; + continue; + } + rdn.nid = child_nid; + ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1); + if (ret == (NIDS_PER_BLOCK + 1)) { + set_nid(page, i, 0, false); + child_nofs += ret; + } else if (ret < 0 && ret != -ENOENT) { + goto out_err; + } + } + freed = child_nofs; + } + + if (!ofs) { + /* remove current indirect node */ + dn->node_page = page; + truncate_node(dn); + freed++; + } else { + f2fs_put_page(page, 1); + } + trace_f2fs_truncate_nodes_exit(dn->inode, freed); + return freed; + +out_err: + f2fs_put_page(page, 1); + trace_f2fs_truncate_nodes_exit(dn->inode, ret); + return ret; +} + +static int truncate_partial_nodes(struct dnode_of_data *dn, + struct f2fs_inode *ri, int *offset, int depth) +{ + struct page *pages[2]; + nid_t nid[3]; + nid_t child_nid; + int err = 0; + int i; + int idx = depth - 2; + + nid[0] = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]); + if (!nid[0]) + return 0; + + /* get indirect nodes in the path */ + for (i = 0; i < idx + 1; i++) { + /* reference count'll be increased */ + pages[i] = get_node_page(F2FS_I_SB(dn->inode), nid[i]); + if (IS_ERR(pages[i])) { + err = PTR_ERR(pages[i]); + idx = i - 1; + goto fail; + } + nid[i + 1] = get_nid(pages[i], offset[i + 1], false); + } + + /* free direct nodes linked to a partial indirect node */ + for (i = offset[idx + 1]; i < NIDS_PER_BLOCK; i++) { + child_nid = get_nid(pages[idx], i, false); + if (!child_nid) + continue; + dn->nid = child_nid; + err = truncate_dnode(dn); + if (err < 0) + goto fail; + set_nid(pages[idx], i, 0, false); + } + + if (offset[idx + 1] == 0) { + dn->node_page = pages[idx]; + dn->nid = nid[idx]; + truncate_node(dn); + } else { + f2fs_put_page(pages[idx], 1); + } + offset[idx]++; + offset[idx + 1] = 0; + idx--; +fail: + for (i = idx; i >= 0; i--) + f2fs_put_page(pages[i], 1); + + trace_f2fs_truncate_partial_nodes(dn->inode, nid, depth, err); + + return err; +} + +/* + * All the block addresses of data and nodes should be nullified. + */ +int truncate_inode_blocks(struct inode *inode, pgoff_t from) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int err = 0, cont = 1; + int level, offset[4], noffset[4]; + unsigned int nofs = 0; + struct f2fs_inode *ri; + struct dnode_of_data dn; + struct page *page; + + trace_f2fs_truncate_inode_blocks_enter(inode, from); + + level = get_node_path(F2FS_I(inode), from, offset, noffset); +restart: + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) { + trace_f2fs_truncate_inode_blocks_exit(inode, PTR_ERR(page)); + return PTR_ERR(page); + } + + set_new_dnode(&dn, inode, page, NULL, 0); + unlock_page(page); + + ri = F2FS_INODE(page); + switch (level) { + case 0: + case 1: + nofs = noffset[1]; + break; + case 2: + nofs = noffset[1]; + if (!offset[level - 1]) + goto skip_partial; + err = truncate_partial_nodes(&dn, ri, offset, level); + if (err < 0 && err != -ENOENT) + goto fail; + nofs += 1 + NIDS_PER_BLOCK; + break; + case 3: + nofs = 5 + 2 * NIDS_PER_BLOCK; + if (!offset[level - 1]) + goto skip_partial; + err = truncate_partial_nodes(&dn, ri, offset, level); + if (err < 0 && err != -ENOENT) + goto fail; + break; + default: + BUG(); + } + +skip_partial: + while (cont) { + dn.nid = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]); + switch (offset[0]) { + case NODE_DIR1_BLOCK: + case NODE_DIR2_BLOCK: + err = truncate_dnode(&dn); + break; + + case NODE_IND1_BLOCK: + case NODE_IND2_BLOCK: + err = truncate_nodes(&dn, nofs, offset[1], 2); + break; + + case NODE_DIND_BLOCK: + err = truncate_nodes(&dn, nofs, offset[1], 3); + cont = 0; + break; + + default: + BUG(); + } + if (err < 0 && err != -ENOENT) + goto fail; + if (offset[1] == 0 && + ri->i_nid[offset[0] - NODE_DIR1_BLOCK]) { + lock_page(page); + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { + f2fs_put_page(page, 1); + goto restart; + } + f2fs_wait_on_page_writeback(page, NODE); + ri->i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; + set_page_dirty(page); + unlock_page(page); + } + offset[1] = 0; + offset[0]++; + nofs += err; + } +fail: + f2fs_put_page(page, 0); + trace_f2fs_truncate_inode_blocks_exit(inode, err); + return err > 0 ? 0 : err; +} + +int truncate_xattr_node(struct inode *inode, struct page *page) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t nid = F2FS_I(inode)->i_xattr_nid; + struct dnode_of_data dn; + struct page *npage; + + if (!nid) + return 0; + + npage = get_node_page(sbi, nid); + if (IS_ERR(npage)) + return PTR_ERR(npage); + + F2FS_I(inode)->i_xattr_nid = 0; + + /* need to do checkpoint during fsync */ + F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); + + set_new_dnode(&dn, inode, page, npage, nid); + + if (page) + dn.inode_page_locked = true; + truncate_node(&dn); + return 0; +} + +/* + * Caller should grab and release a rwsem by calling f2fs_lock_op() and + * f2fs_unlock_op(). + */ +void remove_inode_page(struct inode *inode) +{ + struct dnode_of_data dn; + + set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); + if (get_dnode_of_data(&dn, 0, LOOKUP_NODE)) + return; + + if (truncate_xattr_node(inode, dn.inode_page)) { + f2fs_put_dnode(&dn); + return; + } + + /* remove potential inline_data blocks */ + if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode)) + truncate_data_blocks_range(&dn, 1); + + /* 0 is possible, after f2fs_new_inode() has failed */ + f2fs_bug_on(F2FS_I_SB(inode), + inode->i_blocks != 0 && inode->i_blocks != 1); + + /* will put inode & node pages */ + truncate_node(&dn); +} + +struct page *new_inode_page(struct inode *inode) +{ + struct dnode_of_data dn; + + /* allocate inode page for new inode */ + set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); + + /* caller should f2fs_put_page(page, 1); */ + return new_node_page(&dn, 0, NULL); +} + +struct page *new_node_page(struct dnode_of_data *dn, + unsigned int ofs, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct node_info old_ni, new_ni; + struct page *page; + int err; + + if (unlikely(is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC))) + return ERR_PTR(-EPERM); + + page = grab_cache_page(NODE_MAPPING(sbi), dn->nid); + if (!page) + return ERR_PTR(-ENOMEM); + + if (unlikely(!inc_valid_node_count(sbi, dn->inode))) { + err = -ENOSPC; + goto fail; + } + + get_node_info(sbi, dn->nid, &old_ni); + + /* Reinitialize old_ni with new node page */ + f2fs_bug_on(sbi, old_ni.blk_addr != NULL_ADDR); + new_ni = old_ni; + new_ni.ino = dn->inode->i_ino; + set_node_addr(sbi, &new_ni, NEW_ADDR, false); + + f2fs_wait_on_page_writeback(page, NODE); + fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); + set_cold_node(dn->inode, page); + SetPageUptodate(page); + set_page_dirty(page); + + if (f2fs_has_xattr_block(ofs)) + F2FS_I(dn->inode)->i_xattr_nid = dn->nid; + + dn->node_page = page; + if (ipage) + update_inode(dn->inode, ipage); + else + sync_inode_page(dn); + if (ofs == 0) + inc_valid_inode_count(sbi); + + return page; + +fail: + clear_node_page_dirty(page); + f2fs_put_page(page, 1); + return ERR_PTR(err); +} + +/* + * Caller should do after getting the following values. + * 0: f2fs_put_page(page, 0) + * LOCKED_PAGE: f2fs_put_page(page, 1) + * error: nothing + */ +static int read_node_page(struct page *page, int rw) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(page); + struct node_info ni; + struct f2fs_io_info fio = { + .type = NODE, + .rw = rw, + }; + + get_node_info(sbi, page->index, &ni); + + if (unlikely(ni.blk_addr == NULL_ADDR)) { + ClearPageUptodate(page); + f2fs_put_page(page, 1); + return -ENOENT; + } + + if (PageUptodate(page)) + return LOCKED_PAGE; + + fio.blk_addr = ni.blk_addr; + return f2fs_submit_page_bio(sbi, page, &fio); +} + +/* + * Readahead a node page + */ +void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct page *apage; + int err; + + apage = find_get_page(NODE_MAPPING(sbi), nid); + if (apage && PageUptodate(apage)) { + f2fs_put_page(apage, 0); + return; + } + f2fs_put_page(apage, 0); + + apage = grab_cache_page(NODE_MAPPING(sbi), nid); + if (!apage) + return; + + err = read_node_page(apage, READA); + if (err == 0) + f2fs_put_page(apage, 0); + else if (err == LOCKED_PAGE) + f2fs_put_page(apage, 1); +} + +struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) +{ + struct page *page; + int err; +repeat: + page = grab_cache_page(NODE_MAPPING(sbi), nid); + if (!page) + return ERR_PTR(-ENOMEM); + + err = read_node_page(page, READ_SYNC); + if (err < 0) + return ERR_PTR(err); + else if (err != LOCKED_PAGE) + lock_page(page); + + if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) { + ClearPageUptodate(page); + f2fs_put_page(page, 1); + return ERR_PTR(-EIO); + } + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { + f2fs_put_page(page, 1); + goto repeat; + } + mark_page_accessed(page); + return page; +} + +/* + * Return a locked page for the desired node page. + * And, readahead MAX_RA_NODE number of node pages. + */ +struct page *get_node_page_ra(struct page *parent, int start) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(parent); + struct blk_plug plug; + struct page *page; + int err, i, end; + nid_t nid; + + /* First, try getting the desired direct node. */ + nid = get_nid(parent, start, false); + if (!nid) + return ERR_PTR(-ENOENT); +repeat: + page = grab_cache_page(NODE_MAPPING(sbi), nid); + if (!page) + return ERR_PTR(-ENOMEM); + + err = read_node_page(page, READ_SYNC); + if (err < 0) + return ERR_PTR(err); + else if (err == LOCKED_PAGE) + goto page_hit; + + blk_start_plug(&plug); + + /* Then, try readahead for siblings of the desired node */ + end = start + MAX_RA_NODE; + end = min(end, NIDS_PER_BLOCK); + for (i = start + 1; i < end; i++) { + nid = get_nid(parent, i, false); + if (!nid) + continue; + ra_node_page(sbi, nid); + } + + blk_finish_plug(&plug); + + lock_page(page); + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { + f2fs_put_page(page, 1); + goto repeat; + } +page_hit: + if (unlikely(!PageUptodate(page))) { + f2fs_put_page(page, 1); + return ERR_PTR(-EIO); + } + mark_page_accessed(page); + return page; +} + +void sync_inode_page(struct dnode_of_data *dn) +{ + if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) { + update_inode(dn->inode, dn->node_page); + } else if (dn->inode_page) { + if (!dn->inode_page_locked) + lock_page(dn->inode_page); + update_inode(dn->inode, dn->inode_page); + if (!dn->inode_page_locked) + unlock_page(dn->inode_page); + } else { + update_inode_page(dn->inode); + } +} + +int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, + struct writeback_control *wbc) +{ + pgoff_t index, end; + struct pagevec pvec; + int step = ino ? 2 : 0; + int nwritten = 0, wrote = 0; + + pagevec_init(&pvec, 0); + +next_step: + index = 0; + end = LONG_MAX; + + while (index <= end) { + int i, nr_pages; + nr_pages = pagevec_lookup_tag(&pvec, NODE_MAPPING(sbi), &index, + PAGECACHE_TAG_DIRTY, + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); + if (nr_pages == 0) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + /* + * flushing sequence with step: + * 0. indirect nodes + * 1. dentry dnodes + * 2. file dnodes + */ + if (step == 0 && IS_DNODE(page)) + continue; + if (step == 1 && (!IS_DNODE(page) || + is_cold_node(page))) + continue; + if (step == 2 && (!IS_DNODE(page) || + !is_cold_node(page))) + continue; + + /* + * If an fsync mode, + * we should not skip writing node pages. + */ + if (ino && ino_of_node(page) == ino) + lock_page(page); + else if (!trylock_page(page)) + continue; + + if (unlikely(page->mapping != NODE_MAPPING(sbi))) { +continue_unlock: + unlock_page(page); + continue; + } + if (ino && ino_of_node(page) != ino) + goto continue_unlock; + + if (!PageDirty(page)) { + /* someone wrote it for us */ + goto continue_unlock; + } + + if (!clear_page_dirty_for_io(page)) + goto continue_unlock; + + /* called by fsync() */ + if (ino && IS_DNODE(page)) { + set_fsync_mark(page, 1); + if (IS_INODE(page)) { + if (!is_checkpointed_node(sbi, ino) && + !has_fsynced_inode(sbi, ino)) + set_dentry_mark(page, 1); + else + set_dentry_mark(page, 0); + } + nwritten++; + } else { + set_fsync_mark(page, 0); + set_dentry_mark(page, 0); + } + + if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) + unlock_page(page); + else + wrote++; + + if (--wbc->nr_to_write == 0) + break; + } + pagevec_release(&pvec); + cond_resched(); + + if (wbc->nr_to_write == 0) { + step = 2; + break; + } + } + + if (step < 2) { + step++; + goto next_step; + } + + if (wrote) + f2fs_submit_merged_bio(sbi, NODE, WRITE); + return nwritten; +} + +int wait_on_node_pages_writeback(struct f2fs_sb_info *sbi, nid_t ino) +{ + pgoff_t index = 0, end = LONG_MAX; + struct pagevec pvec; + int ret2 = 0, ret = 0; + + pagevec_init(&pvec, 0); + + while (index <= end) { + int i, nr_pages; + nr_pages = pagevec_lookup_tag(&pvec, NODE_MAPPING(sbi), &index, + PAGECACHE_TAG_WRITEBACK, + min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); + if (nr_pages == 0) + break; + + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + + /* until radix tree lookup accepts end_index */ + if (unlikely(page->index > end)) + continue; + + if (ino && ino_of_node(page) == ino) { + f2fs_wait_on_page_writeback(page, NODE); + if (TestClearPageError(page)) + ret = -EIO; + } + } + pagevec_release(&pvec); + cond_resched(); + } + + if (unlikely(test_and_clear_bit(AS_ENOSPC, &NODE_MAPPING(sbi)->flags))) + ret2 = -ENOSPC; + if (unlikely(test_and_clear_bit(AS_EIO, &NODE_MAPPING(sbi)->flags))) + ret2 = -EIO; + if (!ret) + ret = ret2; + return ret; +} + +static int f2fs_write_node_page(struct page *page, + struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_P_SB(page); + nid_t nid; + struct node_info ni; + struct f2fs_io_info fio = { + .type = NODE, + .rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE, + }; + + trace_f2fs_writepage(page, NODE); + + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + goto redirty_out; + if (unlikely(f2fs_cp_error(sbi))) + goto redirty_out; + + f2fs_wait_on_page_writeback(page, NODE); + + /* get old block addr of this node page */ + nid = nid_of_node(page); + f2fs_bug_on(sbi, page->index != nid); + + get_node_info(sbi, nid, &ni); + + /* This page is already truncated */ + if (unlikely(ni.blk_addr == NULL_ADDR)) { + ClearPageUptodate(page); + dec_page_count(sbi, F2FS_DIRTY_NODES); + unlock_page(page); + return 0; + } + + if (wbc->for_reclaim) { + if (!down_read_trylock(&sbi->node_write)) + goto redirty_out; + } else { + down_read(&sbi->node_write); + } + + set_page_writeback(page); + fio.blk_addr = ni.blk_addr; + write_node_page(sbi, page, nid, &fio); + set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); + dec_page_count(sbi, F2FS_DIRTY_NODES); + up_read(&sbi->node_write); + unlock_page(page); + + if (wbc->for_reclaim) + f2fs_submit_merged_bio(sbi, NODE, WRITE); + + return 0; + +redirty_out: + redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; +} + +static int f2fs_write_node_pages(struct address_space *mapping, + struct writeback_control *wbc) +{ + struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + long diff; + + trace_f2fs_writepages(mapping->host, wbc, NODE); + + /* balancing f2fs's metadata in background */ + f2fs_balance_fs_bg(sbi); + + /* collect a number of dirty node pages and write together */ + if (get_pages(sbi, F2FS_DIRTY_NODES) < nr_pages_to_skip(sbi, NODE)) + goto skip_write; + + diff = nr_pages_to_write(sbi, NODE, wbc); + wbc->sync_mode = WB_SYNC_NONE; + sync_node_pages(sbi, 0, wbc); + wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); + return 0; + +skip_write: + wbc->pages_skipped += get_pages(sbi, F2FS_DIRTY_NODES); + return 0; +} + +static int f2fs_set_node_page_dirty(struct page *page) +{ + trace_f2fs_set_page_dirty(page, NODE); + + SetPageUptodate(page); + if (!PageDirty(page)) { + __set_page_dirty_nobuffers(page); + inc_page_count(F2FS_P_SB(page), F2FS_DIRTY_NODES); + SetPagePrivate(page); + f2fs_trace_pid(page); + return 1; + } + return 0; +} + +/* + * Structure of the f2fs node operations + */ +const struct address_space_operations f2fs_node_aops = { + .writepage = f2fs_write_node_page, + .writepages = f2fs_write_node_pages, + .set_page_dirty = f2fs_set_node_page_dirty, + .invalidatepage = f2fs_invalidate_page, + .releasepage = f2fs_release_page, +}; + +static struct free_nid *__lookup_free_nid_list(struct f2fs_nm_info *nm_i, + nid_t n) +{ + return radix_tree_lookup(&nm_i->free_nid_root, n); +} + +static void __del_from_free_nid_list(struct f2fs_nm_info *nm_i, + struct free_nid *i) +{ + list_del(&i->list); + radix_tree_delete(&nm_i->free_nid_root, i->nid); +} + +static int add_free_nid(struct f2fs_sb_info *sbi, nid_t nid, bool build) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i; + struct nat_entry *ne; + bool allocated = false; + + if (!available_free_memory(sbi, FREE_NIDS)) + return -1; + + /* 0 nid should not be used */ + if (unlikely(nid == 0)) + return 0; + + if (build) { + /* do not add allocated nids */ + down_read(&nm_i->nat_tree_lock); + ne = __lookup_nat_cache(nm_i, nid); + if (ne && + (!get_nat_flag(ne, IS_CHECKPOINTED) || + nat_get_blkaddr(ne) != NULL_ADDR)) + allocated = true; + up_read(&nm_i->nat_tree_lock); + if (allocated) + return 0; + } + + i = f2fs_kmem_cache_alloc(free_nid_slab, GFP_NOFS); + i->nid = nid; + i->state = NID_NEW; + + if (radix_tree_preload(GFP_NOFS)) { + kmem_cache_free(free_nid_slab, i); + return 0; + } + + spin_lock(&nm_i->free_nid_list_lock); + if (radix_tree_insert(&nm_i->free_nid_root, i->nid, i)) { + spin_unlock(&nm_i->free_nid_list_lock); + radix_tree_preload_end(); + kmem_cache_free(free_nid_slab, i); + return 0; + } + list_add_tail(&i->list, &nm_i->free_nid_list); + nm_i->fcnt++; + spin_unlock(&nm_i->free_nid_list_lock); + radix_tree_preload_end(); + return 1; +} + +static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid) +{ + struct free_nid *i; + bool need_free = false; + + spin_lock(&nm_i->free_nid_list_lock); + i = __lookup_free_nid_list(nm_i, nid); + if (i && i->state == NID_NEW) { + __del_from_free_nid_list(nm_i, i); + nm_i->fcnt--; + need_free = true; + } + spin_unlock(&nm_i->free_nid_list_lock); + + if (need_free) + kmem_cache_free(free_nid_slab, i); +} + +static void scan_nat_page(struct f2fs_sb_info *sbi, + struct page *nat_page, nid_t start_nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct f2fs_nat_block *nat_blk = page_address(nat_page); + block_t blk_addr; + int i; + + i = start_nid % NAT_ENTRY_PER_BLOCK; + + for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { + + if (unlikely(start_nid >= nm_i->max_nid)) + break; + + blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); + f2fs_bug_on(sbi, blk_addr == NEW_ADDR); + if (blk_addr == NULL_ADDR) { + if (add_free_nid(sbi, start_nid, true) < 0) + break; + } + } +} + +static void build_free_nids(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + int i = 0; + nid_t nid = nm_i->next_scan_nid; + + /* Enough entries */ + if (nm_i->fcnt > NAT_ENTRY_PER_BLOCK) + return; + + /* readahead nat pages to be scanned */ + ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES, META_NAT); + + while (1) { + struct page *page = get_current_nat_page(sbi, nid); + + scan_nat_page(sbi, page, nid); + f2fs_put_page(page, 1); + + nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK)); + if (unlikely(nid >= nm_i->max_nid)) + nid = 0; + + if (i++ == FREE_NID_PAGES) + break; + } + + /* go to the next free nat pages to find free nids abundantly */ + nm_i->next_scan_nid = nid; + + /* find free nids from current sum_pages */ + mutex_lock(&curseg->curseg_mutex); + for (i = 0; i < nats_in_cursum(sum); i++) { + block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr); + nid = le32_to_cpu(nid_in_journal(sum, i)); + if (addr == NULL_ADDR) + add_free_nid(sbi, nid, true); + else + remove_free_nid(nm_i, nid); + } + mutex_unlock(&curseg->curseg_mutex); +} + +/* + * If this function returns success, caller can obtain a new nid + * from second parameter of this function. + * The returned nid could be used ino as well as nid when inode is created. + */ +bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i = NULL; +retry: + if (unlikely(sbi->total_valid_node_count + 1 > nm_i->available_nids)) + return false; + + spin_lock(&nm_i->free_nid_list_lock); + + /* We should not use stale free nids created by build_free_nids */ + if (nm_i->fcnt && !on_build_free_nids(nm_i)) { + f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list)); + list_for_each_entry(i, &nm_i->free_nid_list, list) + if (i->state == NID_NEW) + break; + + f2fs_bug_on(sbi, i->state != NID_NEW); + *nid = i->nid; + i->state = NID_ALLOC; + nm_i->fcnt--; + spin_unlock(&nm_i->free_nid_list_lock); + return true; + } + spin_unlock(&nm_i->free_nid_list_lock); + + /* Let's scan nat pages and its caches to get free nids */ + mutex_lock(&nm_i->build_lock); + build_free_nids(sbi); + mutex_unlock(&nm_i->build_lock); + goto retry; +} + +/* + * alloc_nid() should be called prior to this function. + */ +void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i; + + spin_lock(&nm_i->free_nid_list_lock); + i = __lookup_free_nid_list(nm_i, nid); + f2fs_bug_on(sbi, !i || i->state != NID_ALLOC); + __del_from_free_nid_list(nm_i, i); + spin_unlock(&nm_i->free_nid_list_lock); + + kmem_cache_free(free_nid_slab, i); +} + +/* + * alloc_nid() should be called prior to this function. + */ +void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i; + bool need_free = false; + + if (!nid) + return; + + spin_lock(&nm_i->free_nid_list_lock); + i = __lookup_free_nid_list(nm_i, nid); + f2fs_bug_on(sbi, !i || i->state != NID_ALLOC); + if (!available_free_memory(sbi, FREE_NIDS)) { + __del_from_free_nid_list(nm_i, i); + need_free = true; + } else { + i->state = NID_NEW; + nm_i->fcnt++; + } + spin_unlock(&nm_i->free_nid_list_lock); + + if (need_free) + kmem_cache_free(free_nid_slab, i); +} + +void recover_inline_xattr(struct inode *inode, struct page *page) +{ + void *src_addr, *dst_addr; + size_t inline_size; + struct page *ipage; + struct f2fs_inode *ri; + + ipage = get_node_page(F2FS_I_SB(inode), inode->i_ino); + f2fs_bug_on(F2FS_I_SB(inode), IS_ERR(ipage)); + + ri = F2FS_INODE(page); + if (!(ri->i_inline & F2FS_INLINE_XATTR)) { + clear_inode_flag(F2FS_I(inode), FI_INLINE_XATTR); + goto update_inode; + } + + dst_addr = inline_xattr_addr(ipage); + src_addr = inline_xattr_addr(page); + inline_size = inline_xattr_size(inode); + + f2fs_wait_on_page_writeback(ipage, NODE); + memcpy(dst_addr, src_addr, inline_size); +update_inode: + update_inode(inode, ipage); + f2fs_put_page(ipage, 1); +} + +void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; + nid_t new_xnid = nid_of_node(page); + struct node_info ni; + + /* 1: invalidate the previous xattr nid */ + if (!prev_xnid) + goto recover_xnid; + + /* Deallocate node address */ + get_node_info(sbi, prev_xnid, &ni); + f2fs_bug_on(sbi, ni.blk_addr == NULL_ADDR); + invalidate_blocks(sbi, ni.blk_addr); + dec_valid_node_count(sbi, inode); + set_node_addr(sbi, &ni, NULL_ADDR, false); + +recover_xnid: + /* 2: allocate new xattr nid */ + if (unlikely(!inc_valid_node_count(sbi, inode))) + f2fs_bug_on(sbi, 1); + + remove_free_nid(NM_I(sbi), new_xnid); + get_node_info(sbi, new_xnid, &ni); + ni.ino = inode->i_ino; + set_node_addr(sbi, &ni, NEW_ADDR, false); + F2FS_I(inode)->i_xattr_nid = new_xnid; + + /* 3: update xattr blkaddr */ + refresh_sit_entry(sbi, NEW_ADDR, blkaddr); + set_node_addr(sbi, &ni, blkaddr, false); + + update_inode_page(inode); +} + +int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_inode *src, *dst; + nid_t ino = ino_of_node(page); + struct node_info old_ni, new_ni; + struct page *ipage; + + get_node_info(sbi, ino, &old_ni); + + if (unlikely(old_ni.blk_addr != NULL_ADDR)) + return -EINVAL; + + ipage = grab_cache_page(NODE_MAPPING(sbi), ino); + if (!ipage) + return -ENOMEM; + + /* Should not use this inode from free nid list */ + remove_free_nid(NM_I(sbi), ino); + + SetPageUptodate(ipage); + fill_node_footer(ipage, ino, ino, 0, true); + + src = F2FS_INODE(page); + dst = F2FS_INODE(ipage); + + memcpy(dst, src, (unsigned long)&src->i_ext - (unsigned long)src); + dst->i_size = 0; + dst->i_blocks = cpu_to_le64(1); + dst->i_links = cpu_to_le32(1); + dst->i_xattr_nid = 0; + dst->i_inline = src->i_inline & F2FS_INLINE_XATTR; + + new_ni = old_ni; + new_ni.ino = ino; + + if (unlikely(!inc_valid_node_count(sbi, NULL))) + WARN_ON(1); + set_node_addr(sbi, &new_ni, NEW_ADDR, false); + inc_valid_inode_count(sbi); + set_page_dirty(ipage); + f2fs_put_page(ipage, 1); + return 0; +} + +int restore_node_summary(struct f2fs_sb_info *sbi, + unsigned int segno, struct f2fs_summary_block *sum) +{ + struct f2fs_node *rn; + struct f2fs_summary *sum_entry; + block_t addr; + int bio_blocks = MAX_BIO_BLOCKS(sbi); + int i, idx, last_offset, nrpages; + + /* scan the node segment */ + last_offset = sbi->blocks_per_seg; + addr = START_BLOCK(sbi, segno); + sum_entry = &sum->entries[0]; + + for (i = 0; i < last_offset; i += nrpages, addr += nrpages) { + nrpages = min(last_offset - i, bio_blocks); + + /* readahead node pages */ + ra_meta_pages(sbi, addr, nrpages, META_POR); + + for (idx = addr; idx < addr + nrpages; idx++) { + struct page *page = get_meta_page(sbi, idx); + + rn = F2FS_NODE(page); + sum_entry->nid = rn->footer.nid; + sum_entry->version = 0; + sum_entry->ofs_in_node = 0; + sum_entry++; + f2fs_put_page(page, 1); + } + + invalidate_mapping_pages(META_MAPPING(sbi), addr, + addr + nrpages); + } + return 0; +} + +static void remove_nats_in_journal(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + int i; + + mutex_lock(&curseg->curseg_mutex); + for (i = 0; i < nats_in_cursum(sum); i++) { + struct nat_entry *ne; + struct f2fs_nat_entry raw_ne; + nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); + + raw_ne = nat_in_journal(sum, i); + + down_write(&nm_i->nat_tree_lock); + ne = __lookup_nat_cache(nm_i, nid); + if (!ne) { + ne = grab_nat_entry(nm_i, nid); + node_info_from_raw_nat(&ne->ni, &raw_ne); + } + __set_nat_cache_dirty(nm_i, ne); + up_write(&nm_i->nat_tree_lock); + } + update_nats_in_cursum(sum, -i); + mutex_unlock(&curseg->curseg_mutex); +} + +static void __adjust_nat_entry_set(struct nat_entry_set *nes, + struct list_head *head, int max) +{ + struct nat_entry_set *cur; + + if (nes->entry_cnt >= max) + goto add_out; + + list_for_each_entry(cur, head, set_list) { + if (cur->entry_cnt >= nes->entry_cnt) { + list_add(&nes->set_list, cur->set_list.prev); + return; + } + } +add_out: + list_add_tail(&nes->set_list, head); +} + +static void __flush_nat_entry_set(struct f2fs_sb_info *sbi, + struct nat_entry_set *set) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + nid_t start_nid = set->set * NAT_ENTRY_PER_BLOCK; + bool to_journal = true; + struct f2fs_nat_block *nat_blk; + struct nat_entry *ne, *cur; + struct page *page = NULL; + struct f2fs_nm_info *nm_i = NM_I(sbi); + + /* + * there are two steps to flush nat entries: + * #1, flush nat entries to journal in current hot data summary block. + * #2, flush nat entries to nat page. + */ + if (!__has_cursum_space(sum, set->entry_cnt, NAT_JOURNAL)) + to_journal = false; + + if (to_journal) { + mutex_lock(&curseg->curseg_mutex); + } else { + page = get_next_nat_page(sbi, start_nid); + nat_blk = page_address(page); + f2fs_bug_on(sbi, !nat_blk); + } + + /* flush dirty nats in nat entry set */ + list_for_each_entry_safe(ne, cur, &set->entry_list, list) { + struct f2fs_nat_entry *raw_ne; + nid_t nid = nat_get_nid(ne); + int offset; + + if (nat_get_blkaddr(ne) == NEW_ADDR) + continue; + + if (to_journal) { + offset = lookup_journal_in_cursum(sum, + NAT_JOURNAL, nid, 1); + f2fs_bug_on(sbi, offset < 0); + raw_ne = &nat_in_journal(sum, offset); + nid_in_journal(sum, offset) = cpu_to_le32(nid); + } else { + raw_ne = &nat_blk->entries[nid - start_nid]; + } + raw_nat_from_node_info(raw_ne, &ne->ni); + + down_write(&NM_I(sbi)->nat_tree_lock); + nat_reset_flag(ne); + __clear_nat_cache_dirty(NM_I(sbi), ne); + up_write(&NM_I(sbi)->nat_tree_lock); + + if (nat_get_blkaddr(ne) == NULL_ADDR) + add_free_nid(sbi, nid, false); + } + + if (to_journal) + mutex_unlock(&curseg->curseg_mutex); + else + f2fs_put_page(page, 1); + + f2fs_bug_on(sbi, set->entry_cnt); + + down_write(&nm_i->nat_tree_lock); + radix_tree_delete(&NM_I(sbi)->nat_set_root, set->set); + up_write(&nm_i->nat_tree_lock); + kmem_cache_free(nat_entry_set_slab, set); +} + +/* + * This function is called during the checkpointing process. + */ +void flush_nat_entries(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct nat_entry_set *setvec[SETVEC_SIZE]; + struct nat_entry_set *set, *tmp; + unsigned int found; + nid_t set_idx = 0; + LIST_HEAD(sets); + + if (!nm_i->dirty_nat_cnt) + return; + /* + * if there are no enough space in journal to store dirty nat + * entries, remove all entries from journal and merge them + * into nat entry set. + */ + if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt, NAT_JOURNAL)) + remove_nats_in_journal(sbi); + + down_write(&nm_i->nat_tree_lock); + while ((found = __gang_lookup_nat_set(nm_i, + set_idx, SETVEC_SIZE, setvec))) { + unsigned idx; + set_idx = setvec[found - 1]->set + 1; + for (idx = 0; idx < found; idx++) + __adjust_nat_entry_set(setvec[idx], &sets, + MAX_NAT_JENTRIES(sum)); + } + up_write(&nm_i->nat_tree_lock); + + /* flush dirty nats in nat entry set */ + list_for_each_entry_safe(set, tmp, &sets, set_list) + __flush_nat_entry_set(sbi, set); + + f2fs_bug_on(sbi, nm_i->dirty_nat_cnt); +} + +static int init_node_manager(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi); + struct f2fs_nm_info *nm_i = NM_I(sbi); + unsigned char *version_bitmap; + unsigned int nat_segs, nat_blocks; + + nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr); + + /* segment_count_nat includes pair segment so divide to 2. */ + nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1; + nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg); + + nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; + + /* not used nids: 0, node, meta, (and root counted as valid node) */ + nm_i->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM; + nm_i->fcnt = 0; + nm_i->nat_cnt = 0; + nm_i->ram_thresh = DEF_RAM_THRESHOLD; + + INIT_RADIX_TREE(&nm_i->free_nid_root, GFP_ATOMIC); + INIT_LIST_HEAD(&nm_i->free_nid_list); + INIT_RADIX_TREE(&nm_i->nat_root, GFP_NOIO); + INIT_RADIX_TREE(&nm_i->nat_set_root, GFP_NOIO); + INIT_LIST_HEAD(&nm_i->nat_entries); + + mutex_init(&nm_i->build_lock); + spin_lock_init(&nm_i->free_nid_list_lock); + init_rwsem(&nm_i->nat_tree_lock); + + nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); + nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); + version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP); + if (!version_bitmap) + return -EFAULT; + + nm_i->nat_bitmap = kmemdup(version_bitmap, nm_i->bitmap_size, + GFP_KERNEL); + if (!nm_i->nat_bitmap) + return -ENOMEM; + return 0; +} + +int build_node_manager(struct f2fs_sb_info *sbi) +{ + int err; + + sbi->nm_info = kzalloc(sizeof(struct f2fs_nm_info), GFP_KERNEL); + if (!sbi->nm_info) + return -ENOMEM; + + err = init_node_manager(sbi); + if (err) + return err; + + build_free_nids(sbi); + return 0; +} + +void destroy_node_manager(struct f2fs_sb_info *sbi) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *i, *next_i; + struct nat_entry *natvec[NATVEC_SIZE]; + struct nat_entry_set *setvec[SETVEC_SIZE]; + nid_t nid = 0; + unsigned int found; + + if (!nm_i) + return; + + /* destroy free nid list */ + spin_lock(&nm_i->free_nid_list_lock); + list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { + f2fs_bug_on(sbi, i->state == NID_ALLOC); + __del_from_free_nid_list(nm_i, i); + nm_i->fcnt--; + spin_unlock(&nm_i->free_nid_list_lock); + kmem_cache_free(free_nid_slab, i); + spin_lock(&nm_i->free_nid_list_lock); + } + f2fs_bug_on(sbi, nm_i->fcnt); + spin_unlock(&nm_i->free_nid_list_lock); + + /* destroy nat cache */ + down_write(&nm_i->nat_tree_lock); + while ((found = __gang_lookup_nat_cache(nm_i, + nid, NATVEC_SIZE, natvec))) { + unsigned idx; + + nid = nat_get_nid(natvec[found - 1]) + 1; + for (idx = 0; idx < found; idx++) + __del_from_nat_cache(nm_i, natvec[idx]); + } + f2fs_bug_on(sbi, nm_i->nat_cnt); + + /* destroy nat set cache */ + nid = 0; + while ((found = __gang_lookup_nat_set(nm_i, + nid, SETVEC_SIZE, setvec))) { + unsigned idx; + + nid = setvec[found - 1]->set + 1; + for (idx = 0; idx < found; idx++) { + /* entry_cnt is not zero, when cp_error was occurred */ + f2fs_bug_on(sbi, !list_empty(&setvec[idx]->entry_list)); + radix_tree_delete(&nm_i->nat_set_root, setvec[idx]->set); + kmem_cache_free(nat_entry_set_slab, setvec[idx]); + } + } + up_write(&nm_i->nat_tree_lock); + + kfree(nm_i->nat_bitmap); + sbi->nm_info = NULL; + kfree(nm_i); +} + +int __init create_node_manager_caches(void) +{ + nat_entry_slab = f2fs_kmem_cache_create("nat_entry", + sizeof(struct nat_entry)); + if (!nat_entry_slab) + goto fail; + + free_nid_slab = f2fs_kmem_cache_create("free_nid", + sizeof(struct free_nid)); + if (!free_nid_slab) + goto destroy_nat_entry; + + nat_entry_set_slab = f2fs_kmem_cache_create("nat_entry_set", + sizeof(struct nat_entry_set)); + if (!nat_entry_set_slab) + goto destroy_free_nid; + return 0; + +destroy_free_nid: + kmem_cache_destroy(free_nid_slab); +destroy_nat_entry: + kmem_cache_destroy(nat_entry_slab); +fail: + return -ENOMEM; +} + +void destroy_node_manager_caches(void) +{ + kmem_cache_destroy(nat_entry_set_slab); + kmem_cache_destroy(free_nid_slab); + kmem_cache_destroy(nat_entry_slab); +} diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h new file mode 100644 index 000000000000..c56026f1725c --- /dev/null +++ b/fs/f2fs/node.h @@ -0,0 +1,416 @@ +/* + * fs/f2fs/node.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +/* start node id of a node block dedicated to the given node id */ +#define START_NID(nid) ((nid / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK) + +/* node block offset on the NAT area dedicated to the given start node id */ +#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) + +/* # of pages to perform readahead before building free nids */ +#define FREE_NID_PAGES 4 + +/* maximum readahead size for node during getting data blocks */ +#define MAX_RA_NODE 128 + +/* control the memory footprint threshold (10MB per 1GB ram) */ +#define DEF_RAM_THRESHOLD 10 + +/* vector size for gang look-up from nat cache that consists of radix tree */ +#define NATVEC_SIZE 64 +#define SETVEC_SIZE 32 + +/* return value for read_node_page */ +#define LOCKED_PAGE 1 + +/* For flag in struct node_info */ +enum { + IS_CHECKPOINTED, /* is it checkpointed before? */ + HAS_FSYNCED_INODE, /* is the inode fsynced before? */ + HAS_LAST_FSYNC, /* has the latest node fsync mark? */ + IS_DIRTY, /* this nat entry is dirty? */ +}; + +/* + * For node information + */ +struct node_info { + nid_t nid; /* node id */ + nid_t ino; /* inode number of the node's owner */ + block_t blk_addr; /* block address of the node */ + unsigned char version; /* version of the node */ + unsigned char flag; /* for node information bits */ +}; + +struct nat_entry { + struct list_head list; /* for clean or dirty nat list */ + struct node_info ni; /* in-memory node information */ +}; + +#define nat_get_nid(nat) (nat->ni.nid) +#define nat_set_nid(nat, n) (nat->ni.nid = n) +#define nat_get_blkaddr(nat) (nat->ni.blk_addr) +#define nat_set_blkaddr(nat, b) (nat->ni.blk_addr = b) +#define nat_get_ino(nat) (nat->ni.ino) +#define nat_set_ino(nat, i) (nat->ni.ino = i) +#define nat_get_version(nat) (nat->ni.version) +#define nat_set_version(nat, v) (nat->ni.version = v) + +#define inc_node_version(version) (++version) + +static inline void copy_node_info(struct node_info *dst, + struct node_info *src) +{ + dst->nid = src->nid; + dst->ino = src->ino; + dst->blk_addr = src->blk_addr; + dst->version = src->version; + /* should not copy flag here */ +} + +static inline void set_nat_flag(struct nat_entry *ne, + unsigned int type, bool set) +{ + unsigned char mask = 0x01 << type; + if (set) + ne->ni.flag |= mask; + else + ne->ni.flag &= ~mask; +} + +static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type) +{ + unsigned char mask = 0x01 << type; + return ne->ni.flag & mask; +} + +static inline void nat_reset_flag(struct nat_entry *ne) +{ + /* these states can be set only after checkpoint was done */ + set_nat_flag(ne, IS_CHECKPOINTED, true); + set_nat_flag(ne, HAS_FSYNCED_INODE, false); + set_nat_flag(ne, HAS_LAST_FSYNC, true); +} + +static inline void node_info_from_raw_nat(struct node_info *ni, + struct f2fs_nat_entry *raw_ne) +{ + ni->ino = le32_to_cpu(raw_ne->ino); + ni->blk_addr = le32_to_cpu(raw_ne->block_addr); + ni->version = raw_ne->version; +} + +static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne, + struct node_info *ni) +{ + raw_ne->ino = cpu_to_le32(ni->ino); + raw_ne->block_addr = cpu_to_le32(ni->blk_addr); + raw_ne->version = ni->version; +} + +enum mem_type { + FREE_NIDS, /* indicates the free nid list */ + NAT_ENTRIES, /* indicates the cached nat entry */ + DIRTY_DENTS, /* indicates dirty dentry pages */ + INO_ENTRIES, /* indicates inode entries */ + EXTENT_CACHE, /* indicates extent cache */ + BASE_CHECK, /* check kernel status */ +}; + +struct nat_entry_set { + struct list_head set_list; /* link with other nat sets */ + struct list_head entry_list; /* link with dirty nat entries */ + nid_t set; /* set number*/ + unsigned int entry_cnt; /* the # of nat entries in set */ +}; + +/* + * For free nid mangement + */ +enum nid_state { + NID_NEW, /* newly added to free nid list */ + NID_ALLOC /* it is allocated */ +}; + +struct free_nid { + struct list_head list; /* for free node id list */ + nid_t nid; /* node id */ + int state; /* in use or not: NID_NEW or NID_ALLOC */ +}; + +static inline void next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct free_nid *fnid; + + spin_lock(&nm_i->free_nid_list_lock); + if (nm_i->fcnt <= 0) { + spin_unlock(&nm_i->free_nid_list_lock); + return; + } + fnid = list_entry(nm_i->free_nid_list.next, struct free_nid, list); + *nid = fnid->nid; + spin_unlock(&nm_i->free_nid_list_lock); +} + +/* + * inline functions + */ +static inline void get_nat_bitmap(struct f2fs_sb_info *sbi, void *addr) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + memcpy(addr, nm_i->nat_bitmap, nm_i->bitmap_size); +} + +static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + pgoff_t block_off; + pgoff_t block_addr; + int seg_off; + + block_off = NAT_BLOCK_OFFSET(start); + seg_off = block_off >> sbi->log_blocks_per_seg; + + block_addr = (pgoff_t)(nm_i->nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) + block_addr += sbi->blocks_per_seg; + + return block_addr; +} + +static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi, + pgoff_t block_addr) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + + block_addr -= nm_i->nat_blkaddr; + if ((block_addr >> sbi->log_blocks_per_seg) % 2) + block_addr -= sbi->blocks_per_seg; + else + block_addr += sbi->blocks_per_seg; + + return block_addr + nm_i->nat_blkaddr; +} + +static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid) +{ + unsigned int block_off = NAT_BLOCK_OFFSET(start_nid); + + f2fs_change_bit(block_off, nm_i->nat_bitmap); +} + +static inline void fill_node_footer(struct page *page, nid_t nid, + nid_t ino, unsigned int ofs, bool reset) +{ + struct f2fs_node *rn = F2FS_NODE(page); + unsigned int old_flag = 0; + + if (reset) + memset(rn, 0, sizeof(*rn)); + else + old_flag = le32_to_cpu(rn->footer.flag); + + rn->footer.nid = cpu_to_le32(nid); + rn->footer.ino = cpu_to_le32(ino); + + /* should remain old flag bits such as COLD_BIT_SHIFT */ + rn->footer.flag = cpu_to_le32((ofs << OFFSET_BIT_SHIFT) | + (old_flag & OFFSET_BIT_MASK)); +} + +static inline void copy_node_footer(struct page *dst, struct page *src) +{ + struct f2fs_node *src_rn = F2FS_NODE(src); + struct f2fs_node *dst_rn = F2FS_NODE(dst); + memcpy(&dst_rn->footer, &src_rn->footer, sizeof(struct node_footer)); +} + +static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(F2FS_P_SB(page)); + struct f2fs_node *rn = F2FS_NODE(page); + + rn->footer.cp_ver = ckpt->checkpoint_ver; + rn->footer.next_blkaddr = cpu_to_le32(blkaddr); +} + +static inline nid_t ino_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.ino); +} + +static inline nid_t nid_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.nid); +} + +static inline unsigned int ofs_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + unsigned flag = le32_to_cpu(rn->footer.flag); + return flag >> OFFSET_BIT_SHIFT; +} + +static inline unsigned long long cpver_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le64_to_cpu(rn->footer.cp_ver); +} + +static inline block_t next_blkaddr_of_node(struct page *node_page) +{ + struct f2fs_node *rn = F2FS_NODE(node_page); + return le32_to_cpu(rn->footer.next_blkaddr); +} + +/* + * f2fs assigns the following node offsets described as (num). + * N = NIDS_PER_BLOCK + * + * Inode block (0) + * |- direct node (1) + * |- direct node (2) + * |- indirect node (3) + * | `- direct node (4 => 4 + N - 1) + * |- indirect node (4 + N) + * | `- direct node (5 + N => 5 + 2N - 1) + * `- double indirect node (5 + 2N) + * `- indirect node (6 + 2N) + * `- direct node + * ...... + * `- indirect node ((6 + 2N) + x(N + 1)) + * `- direct node + * ...... + * `- indirect node ((6 + 2N) + (N - 1)(N + 1)) + * `- direct node + */ +static inline bool IS_DNODE(struct page *node_page) +{ + unsigned int ofs = ofs_of_node(node_page); + + if (f2fs_has_xattr_block(ofs)) + return false; + + if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK || + ofs == 5 + 2 * NIDS_PER_BLOCK) + return false; + if (ofs >= 6 + 2 * NIDS_PER_BLOCK) { + ofs -= 6 + 2 * NIDS_PER_BLOCK; + if (!((long int)ofs % (NIDS_PER_BLOCK + 1))) + return false; + } + return true; +} + +static inline void set_nid(struct page *p, int off, nid_t nid, bool i) +{ + struct f2fs_node *rn = F2FS_NODE(p); + + f2fs_wait_on_page_writeback(p, NODE); + + if (i) + rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); + else + rn->in.nid[off] = cpu_to_le32(nid); + set_page_dirty(p); +} + +static inline nid_t get_nid(struct page *p, int off, bool i) +{ + struct f2fs_node *rn = F2FS_NODE(p); + + if (i) + return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]); + return le32_to_cpu(rn->in.nid[off]); +} + +/* + * Coldness identification: + * - Mark cold files in f2fs_inode_info + * - Mark cold node blocks in their node footer + * - Mark cold data pages in page cache + */ +static inline int is_file(struct inode *inode, int type) +{ + return F2FS_I(inode)->i_advise & type; +} + +static inline void set_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise |= type; +} + +static inline void clear_file(struct inode *inode, int type) +{ + F2FS_I(inode)->i_advise &= ~type; +} + +#define file_is_cold(inode) is_file(inode, FADVISE_COLD_BIT) +#define file_wrong_pino(inode) is_file(inode, FADVISE_LOST_PINO_BIT) +#define file_set_cold(inode) set_file(inode, FADVISE_COLD_BIT) +#define file_lost_pino(inode) set_file(inode, FADVISE_LOST_PINO_BIT) +#define file_clear_cold(inode) clear_file(inode, FADVISE_COLD_BIT) +#define file_got_pino(inode) clear_file(inode, FADVISE_LOST_PINO_BIT) + +static inline int is_cold_data(struct page *page) +{ + return PageChecked(page); +} + +static inline void set_cold_data(struct page *page) +{ + SetPageChecked(page); +} + +static inline void clear_cold_data(struct page *page) +{ + ClearPageChecked(page); +} + +static inline int is_node(struct page *page, int type) +{ + struct f2fs_node *rn = F2FS_NODE(page); + return le32_to_cpu(rn->footer.flag) & (1 << type); +} + +#define is_cold_node(page) is_node(page, COLD_BIT_SHIFT) +#define is_fsync_dnode(page) is_node(page, FSYNC_BIT_SHIFT) +#define is_dent_dnode(page) is_node(page, DENT_BIT_SHIFT) + +static inline void set_cold_node(struct inode *inode, struct page *page) +{ + struct f2fs_node *rn = F2FS_NODE(page); + unsigned int flag = le32_to_cpu(rn->footer.flag); + + if (S_ISDIR(inode->i_mode)) + flag &= ~(0x1 << COLD_BIT_SHIFT); + else + flag |= (0x1 << COLD_BIT_SHIFT); + rn->footer.flag = cpu_to_le32(flag); +} + +static inline void set_mark(struct page *page, int mark, int type) +{ + struct f2fs_node *rn = F2FS_NODE(page); + unsigned int flag = le32_to_cpu(rn->footer.flag); + if (mark) + flag |= (0x1 << type); + else + flag &= ~(0x1 << type); + rn->footer.flag = cpu_to_le32(flag); +} +#define set_dentry_mark(page, mark) set_mark(page, mark, DENT_BIT_SHIFT) +#define set_fsync_mark(page, mark) set_mark(page, mark, FSYNC_BIT_SHIFT) diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c new file mode 100644 index 000000000000..0d4866b7c8b5 --- /dev/null +++ b/fs/f2fs/recovery.c @@ -0,0 +1,575 @@ +/* + * fs/f2fs/recovery.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include "f2fs.h" +#include "node.h" +#include "segment.h" + +/* + * Roll forward recovery scenarios. + * + * [Term] F: fsync_mark, D: dentry_mark + * + * 1. inode(x) | CP | inode(x) | dnode(F) + * -> Update the latest inode(x). + * + * 2. inode(x) | CP | inode(F) | dnode(F) + * -> No problem. + * + * 3. inode(x) | CP | dnode(F) | inode(x) + * -> Recover to the latest dnode(F), and drop the last inode(x) + * + * 4. inode(x) | CP | dnode(F) | inode(F) + * -> No problem. + * + * 5. CP | inode(x) | dnode(F) + * -> The inode(DF) was missing. Should drop this dnode(F). + * + * 6. CP | inode(DF) | dnode(F) + * -> No problem. + * + * 7. CP | dnode(F) | inode(DF) + * -> If f2fs_iget fails, then goto next to find inode(DF). + * + * 8. CP | dnode(F) | inode(x) + * -> If f2fs_iget fails, then goto next to find inode(DF). + * But it will fail due to no inode(DF). + */ + +static struct kmem_cache *fsync_entry_slab; + +bool space_for_roll_forward(struct f2fs_sb_info *sbi) +{ + if (sbi->last_valid_block_count + sbi->alloc_valid_block_count + > sbi->user_block_count) + return false; + return true; +} + +static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, + nid_t ino) +{ + struct fsync_inode_entry *entry; + + list_for_each_entry(entry, head, list) + if (entry->inode->i_ino == ino) + return entry; + + return NULL; +} + +static int recover_dentry(struct inode *inode, struct page *ipage) +{ + struct f2fs_inode *raw_inode = F2FS_INODE(ipage); + nid_t pino = le32_to_cpu(raw_inode->i_pino); + struct f2fs_dir_entry *de; + struct qstr name; + struct page *page; + struct inode *dir, *einode; + int err = 0; + + dir = f2fs_iget(inode->i_sb, pino); + if (IS_ERR(dir)) { + err = PTR_ERR(dir); + goto out; + } + + name.len = le32_to_cpu(raw_inode->i_namelen); + name.name = raw_inode->i_name; + + if (unlikely(name.len > F2FS_NAME_LEN)) { + WARN_ON(1); + err = -ENAMETOOLONG; + goto out_err; + } +retry: + de = f2fs_find_entry(dir, &name, &page); + if (de && inode->i_ino == le32_to_cpu(de->ino)) + goto out_unmap_put; + + if (de) { + einode = f2fs_iget(inode->i_sb, le32_to_cpu(de->ino)); + if (IS_ERR(einode)) { + WARN_ON(1); + err = PTR_ERR(einode); + if (err == -ENOENT) + err = -EEXIST; + goto out_unmap_put; + } + err = acquire_orphan_inode(F2FS_I_SB(inode)); + if (err) { + iput(einode); + goto out_unmap_put; + } + f2fs_delete_entry(de, page, dir, einode); + iput(einode); + goto retry; + } + err = __f2fs_add_link(dir, &name, inode, inode->i_ino, inode->i_mode); + if (err) + goto out_err; + + if (is_inode_flag_set(F2FS_I(dir), FI_DELAY_IPUT)) { + iput(dir); + } else { + add_dirty_dir_inode(dir); + set_inode_flag(F2FS_I(dir), FI_DELAY_IPUT); + } + + goto out; + +out_unmap_put: + f2fs_dentry_kunmap(dir, page); + f2fs_put_page(page, 0); +out_err: + iput(dir); +out: + f2fs_msg(inode->i_sb, KERN_NOTICE, + "%s: ino = %x, name = %s, dir = %lx, err = %d", + __func__, ino_of_node(ipage), raw_inode->i_name, + IS_ERR(dir) ? 0 : dir->i_ino, err); + return err; +} + +static void recover_inode(struct inode *inode, struct page *page) +{ + struct f2fs_inode *raw = F2FS_INODE(page); + + inode->i_mode = le16_to_cpu(raw->i_mode); + i_size_write(inode, le64_to_cpu(raw->i_size)); + inode->i_atime.tv_sec = le64_to_cpu(raw->i_mtime); + inode->i_ctime.tv_sec = le64_to_cpu(raw->i_ctime); + inode->i_mtime.tv_sec = le64_to_cpu(raw->i_mtime); + inode->i_atime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec); + inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec); + + f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s", + ino_of_node(page), F2FS_INODE(page)->i_name); +} + +static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) +{ + unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); + struct curseg_info *curseg; + struct page *page = NULL; + block_t blkaddr; + int err = 0; + + /* get node pages in the current segment */ + curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); + blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + + ra_meta_pages(sbi, blkaddr, 1, META_POR); + + while (1) { + struct fsync_inode_entry *entry; + + if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + return 0; + + page = get_meta_page(sbi, blkaddr); + + if (cp_ver != cpver_of_node(page)) + break; + + if (!is_fsync_dnode(page)) + goto next; + + entry = get_fsync_inode(head, ino_of_node(page)); + if (!entry) { + if (IS_INODE(page) && is_dent_dnode(page)) { + err = recover_inode_page(sbi, page); + if (err) + break; + } + + /* add this fsync inode to the list */ + entry = kmem_cache_alloc(fsync_entry_slab, GFP_F2FS_ZERO); + if (!entry) { + err = -ENOMEM; + break; + } + /* + * CP | dnode(F) | inode(DF) + * For this case, we should not give up now. + */ + entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); + if (IS_ERR(entry->inode)) { + err = PTR_ERR(entry->inode); + kmem_cache_free(fsync_entry_slab, entry); + if (err == -ENOENT) { + err = 0; + goto next; + } + break; + } + list_add_tail(&entry->list, head); + } + entry->blkaddr = blkaddr; + + if (IS_INODE(page)) { + entry->last_inode = blkaddr; + if (is_dent_dnode(page)) + entry->last_dentry = blkaddr; + } +next: + /* check next segment */ + blkaddr = next_blkaddr_of_node(page); + f2fs_put_page(page, 1); + + ra_meta_pages_cond(sbi, blkaddr); + } + f2fs_put_page(page, 1); + return err; +} + +static void destroy_fsync_dnodes(struct list_head *head) +{ + struct fsync_inode_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, head, list) { + iput(entry->inode); + list_del(&entry->list); + kmem_cache_free(fsync_entry_slab, entry); + } +} + +static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, + block_t blkaddr, struct dnode_of_data *dn) +{ + struct seg_entry *sentry; + unsigned int segno = GET_SEGNO(sbi, blkaddr); + unsigned short blkoff = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); + struct f2fs_summary_block *sum_node; + struct f2fs_summary sum; + struct page *sum_page, *node_page; + struct dnode_of_data tdn = *dn; + nid_t ino, nid; + struct inode *inode; + unsigned int offset; + block_t bidx; + int i; + + sentry = get_seg_entry(sbi, segno); + if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) + return 0; + + /* Get the previous summary */ + for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + if (curseg->segno == segno) { + sum = curseg->sum_blk->entries[blkoff]; + goto got_it; + } + } + + sum_page = get_sum_page(sbi, segno); + sum_node = (struct f2fs_summary_block *)page_address(sum_page); + sum = sum_node->entries[blkoff]; + f2fs_put_page(sum_page, 1); +got_it: + /* Use the locked dnode page and inode */ + nid = le32_to_cpu(sum.nid); + if (dn->inode->i_ino == nid) { + tdn.nid = nid; + if (!dn->inode_page_locked) + lock_page(dn->inode_page); + tdn.node_page = dn->inode_page; + tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); + goto truncate_out; + } else if (dn->nid == nid) { + tdn.ofs_in_node = le16_to_cpu(sum.ofs_in_node); + goto truncate_out; + } + + /* Get the node page */ + node_page = get_node_page(sbi, nid); + if (IS_ERR(node_page)) + return PTR_ERR(node_page); + + offset = ofs_of_node(node_page); + ino = ino_of_node(node_page); + f2fs_put_page(node_page, 1); + + if (ino != dn->inode->i_ino) { + /* Deallocate previous index in the node page */ + inode = f2fs_iget(sbi->sb, ino); + if (IS_ERR(inode)) + return PTR_ERR(inode); + } else { + inode = dn->inode; + } + + bidx = start_bidx_of_node(offset, F2FS_I(inode)) + + le16_to_cpu(sum.ofs_in_node); + + /* + * if inode page is locked, unlock temporarily, but its reference + * count keeps alive. + */ + if (ino == dn->inode->i_ino && dn->inode_page_locked) + unlock_page(dn->inode_page); + + set_new_dnode(&tdn, inode, NULL, NULL, 0); + if (get_dnode_of_data(&tdn, bidx, LOOKUP_NODE)) + goto out; + + if (tdn.data_blkaddr == blkaddr) + truncate_data_blocks_range(&tdn, 1); + + f2fs_put_dnode(&tdn); +out: + if (ino != dn->inode->i_ino) + iput(inode); + else if (dn->inode_page_locked) + lock_page(dn->inode_page); + return 0; + +truncate_out: + if (datablock_addr(tdn.node_page, tdn.ofs_in_node) == blkaddr) + truncate_data_blocks_range(&tdn, 1); + if (dn->inode->i_ino == nid && !dn->inode_page_locked) + unlock_page(dn->inode_page); + return 0; +} + +static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, + struct page *page, block_t blkaddr) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + unsigned int start, end; + struct dnode_of_data dn; + struct f2fs_summary sum; + struct node_info ni; + int err = 0, recovered = 0; + + /* step 1: recover xattr */ + if (IS_INODE(page)) { + recover_inline_xattr(inode, page); + } else if (f2fs_has_xattr_block(ofs_of_node(page))) { + /* + * Deprecated; xattr blocks should be found from cold log. + * But, we should remain this for backward compatibility. + */ + recover_xattr_data(inode, page, blkaddr); + goto out; + } + + /* step 2: recover inline data */ + if (recover_inline_data(inode, page)) + goto out; + + /* step 3: recover data indices */ + start = start_bidx_of_node(ofs_of_node(page), fi); + end = start + ADDRS_PER_PAGE(page, fi); + + f2fs_lock_op(sbi); + + set_new_dnode(&dn, inode, NULL, NULL, 0); + + err = get_dnode_of_data(&dn, start, ALLOC_NODE); + if (err) { + f2fs_unlock_op(sbi); + goto out; + } + + f2fs_wait_on_page_writeback(dn.node_page, NODE); + + get_node_info(sbi, dn.nid, &ni); + f2fs_bug_on(sbi, ni.ino != ino_of_node(page)); + f2fs_bug_on(sbi, ofs_of_node(dn.node_page) != ofs_of_node(page)); + + for (; start < end; start++) { + block_t src, dest; + + src = datablock_addr(dn.node_page, dn.ofs_in_node); + dest = datablock_addr(page, dn.ofs_in_node); + + if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR && + dest >= MAIN_BLKADDR(sbi) && dest < MAX_BLKADDR(sbi)) { + + if (src == NULL_ADDR) { + err = reserve_new_block(&dn); + /* We should not get -ENOSPC */ + f2fs_bug_on(sbi, err); + } + + /* Check the previous node page having this index */ + err = check_index_in_prev_nodes(sbi, dest, &dn); + if (err) + goto err; + + set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); + + /* write dummy data page */ + recover_data_page(sbi, NULL, &sum, src, dest); + dn.data_blkaddr = dest; + set_data_blkaddr(&dn); + f2fs_update_extent_cache(&dn); + recovered++; + } + dn.ofs_in_node++; + } + + if (IS_INODE(dn.node_page)) + sync_inode_page(&dn); + + copy_node_footer(dn.node_page, page); + fill_node_footer(dn.node_page, dn.nid, ni.ino, + ofs_of_node(page), false); + set_page_dirty(dn.node_page); +err: + f2fs_put_dnode(&dn); + f2fs_unlock_op(sbi); +out: + f2fs_msg(sbi->sb, KERN_NOTICE, + "recover_data: ino = %lx, recovered = %d blocks, err = %d", + inode->i_ino, recovered, err); + return err; +} + +static int recover_data(struct f2fs_sb_info *sbi, + struct list_head *head, int type) +{ + unsigned long long cp_ver = cur_cp_version(F2FS_CKPT(sbi)); + struct curseg_info *curseg; + struct page *page = NULL; + int err = 0; + block_t blkaddr; + + /* get node pages in the current segment */ + curseg = CURSEG_I(sbi, type); + blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + + while (1) { + struct fsync_inode_entry *entry; + + if (blkaddr < MAIN_BLKADDR(sbi) || blkaddr >= MAX_BLKADDR(sbi)) + break; + + ra_meta_pages_cond(sbi, blkaddr); + + page = get_meta_page(sbi, blkaddr); + + if (cp_ver != cpver_of_node(page)) { + f2fs_put_page(page, 1); + break; + } + + entry = get_fsync_inode(head, ino_of_node(page)); + if (!entry) + goto next; + /* + * inode(x) | CP | inode(x) | dnode(F) + * In this case, we can lose the latest inode(x). + * So, call recover_inode for the inode update. + */ + if (entry->last_inode == blkaddr) + recover_inode(entry->inode, page); + if (entry->last_dentry == blkaddr) { + err = recover_dentry(entry->inode, page); + if (err) { + f2fs_put_page(page, 1); + break; + } + } + err = do_recover_data(sbi, entry->inode, page, blkaddr); + if (err) { + f2fs_put_page(page, 1); + break; + } + + if (entry->blkaddr == blkaddr) { + iput(entry->inode); + list_del(&entry->list); + kmem_cache_free(fsync_entry_slab, entry); + } +next: + /* check next segment */ + blkaddr = next_blkaddr_of_node(page); + f2fs_put_page(page, 1); + } + if (!err) + allocate_new_segments(sbi); + return err; +} + +int recover_fsync_data(struct f2fs_sb_info *sbi) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); + struct list_head inode_list; + block_t blkaddr; + int err; + bool need_writecp = false; + + fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", + sizeof(struct fsync_inode_entry)); + if (!fsync_entry_slab) + return -ENOMEM; + + INIT_LIST_HEAD(&inode_list); + + /* step #1: find fsynced inode numbers */ + set_sbi_flag(sbi, SBI_POR_DOING); + + /* prevent checkpoint */ + mutex_lock(&sbi->cp_mutex); + + blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + + err = find_fsync_dnodes(sbi, &inode_list); + if (err) + goto out; + + if (list_empty(&inode_list)) + goto out; + + need_writecp = true; + + /* step #2: recover data */ + err = recover_data(sbi, &inode_list, CURSEG_WARM_NODE); + if (!err) + f2fs_bug_on(sbi, !list_empty(&inode_list)); +out: + destroy_fsync_dnodes(&inode_list); + kmem_cache_destroy(fsync_entry_slab); + + /* truncate meta pages to be used by the recovery */ + truncate_inode_pages_range(META_MAPPING(sbi), + MAIN_BLKADDR(sbi) << PAGE_CACHE_SHIFT, -1); + + if (err) { + truncate_inode_pages(NODE_MAPPING(sbi), 0); + truncate_inode_pages(META_MAPPING(sbi), 0); + } + + clear_sbi_flag(sbi, SBI_POR_DOING); + if (err) { + discard_next_dnode(sbi, blkaddr); + + /* Flush all the NAT/SIT pages */ + while (get_pages(sbi, F2FS_DIRTY_META)) + sync_meta_pages(sbi, META, LONG_MAX); + set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + mutex_unlock(&sbi->cp_mutex); + } else if (need_writecp) { + struct cp_control cpc = { + .reason = CP_RECOVERY, + }; + mutex_unlock(&sbi->cp_mutex); + write_checkpoint(sbi, &cpc); + } else { + mutex_unlock(&sbi->cp_mutex); + } + return err; +} diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c new file mode 100644 index 000000000000..4aff9ea564ad --- /dev/null +++ b/fs/f2fs/segment.c @@ -0,0 +1,2393 @@ +/* + * fs/f2fs/segment.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "segment.h" +#include "node.h" +#include "trace.h" +#include + +#define __reverse_ffz(x) __reverse_ffs(~(x)) + +static struct kmem_cache *discard_entry_slab; +static struct kmem_cache *sit_entry_set_slab; +static struct kmem_cache *inmem_entry_slab; + +/** + * Copied from latest lib/llist.c + * llist_for_each_entry_safe - iterate over some deleted entries of + * lock-less list of given type + * safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @node: the first entry of deleted list entries. + * @member: the name of the llist_node with the struct. + * + * In general, some entries of the lock-less list can be traversed + * safely only after being removed from list, so start with an entry + * instead of list head. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each_entry_safe(pos, n, node, member) \ + for (pos = llist_entry((node), typeof(*pos), member); \ + &pos->member != NULL && \ + (n = llist_entry(pos->member.next, typeof(*n), member), true); \ + pos = n) + +/** + * Copied from latest lib/llist.c + * llist_reverse_order - reverse order of a llist chain + * @head: first item of the list to be reversed + * + * Reverse the order of a chain of llist entries and return the + * new first entry. + */ +struct llist_node *llist_reverse_order(struct llist_node *head) +{ + struct llist_node *new_head = NULL; + + while (head) { + struct llist_node *tmp = head; + head = head->next; + tmp->next = new_head; + new_head = tmp; + } + + return new_head; +} + +/** + * Copied from latest linux/list.h + * list_last_entry - get the last element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_last_entry(ptr, type, member) \ + list_entry((ptr)->prev, type, member) + +/* + * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since + * MSB and LSB are reversed in a byte by f2fs_set_bit. + */ +static inline unsigned long __reverse_ffs(unsigned long word) +{ + int num = 0; + +#if BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf0) == 0) + num += 4; + else + word >>= 4; + if ((word & 0xc) == 0) + num += 2; + else + word >>= 2; + if ((word & 0x2) == 0) + num += 1; + return num; +} + +/* + * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because + * f2fs_set_bit makes MSB and LSB reversed in a byte. + * Example: + * LSB <--> MSB + * f2fs_set_bit(0, bitmap) => 0000 0001 + * f2fs_set_bit(7, bitmap) => 1000 0000 + */ +static unsigned long __find_rev_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + const unsigned long *p = addr + BIT_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long tmp; + unsigned long mask, submask; + unsigned long quot, rest; + + if (offset >= size) + return size; + + size -= result; + offset %= BITS_PER_LONG; + if (!offset) + goto aligned; + + tmp = *(p++); + quot = (offset >> 3) << 3; + rest = offset & 0x7; + mask = ~0UL << quot; + submask = (unsigned char)(0xff << rest) >> rest; + submask <<= quot; + mask &= submask; + tmp &= mask; + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + + size -= BITS_PER_LONG; + result += BITS_PER_LONG; +aligned: + while (size & ~(BITS_PER_LONG-1)) { + tmp = *(p++); + if (tmp) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __reverse_ffs(tmp); +} + +static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + const unsigned long *p = addr + BIT_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long tmp; + unsigned long mask, submask; + unsigned long quot, rest; + + if (offset >= size) + return size; + + size -= result; + offset %= BITS_PER_LONG; + if (!offset) + goto aligned; + + tmp = *(p++); + quot = (offset >> 3) << 3; + rest = offset & 0x7; + mask = ~(~0UL << quot); + submask = (unsigned char)~((unsigned char)(0xff << rest) >> rest); + submask <<= quot; + mask += submask; + tmp |= mask; + if (size < BITS_PER_LONG) + goto found_first; + if (~tmp) + goto found_middle; + + size -= BITS_PER_LONG; + result += BITS_PER_LONG; +aligned: + while (size & ~(BITS_PER_LONG - 1)) { + tmp = *(p++); + if (~tmp) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + __reverse_ffz(tmp); +} + +void register_inmem_page(struct inode *inode, struct page *page) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct inmem_pages *new; + int err; + + SetPagePrivate(page); + f2fs_trace_pid(page); + + new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS); + + /* add atomic page indices to the list */ + new->page = page; + INIT_LIST_HEAD(&new->list); +retry: + /* increase reference count with clean state */ + mutex_lock(&fi->inmem_lock); + err = radix_tree_insert(&fi->inmem_root, page->index, new); + if (err == -EEXIST) { + mutex_unlock(&fi->inmem_lock); + kmem_cache_free(inmem_entry_slab, new); + return; + } else if (err) { + mutex_unlock(&fi->inmem_lock); + goto retry; + } + get_page(page); + list_add_tail(&new->list, &fi->inmem_pages); + inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); + mutex_unlock(&fi->inmem_lock); + + trace_f2fs_register_inmem_page(page, INMEM); +} + +void commit_inmem_pages(struct inode *inode, bool abort) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_inode_info *fi = F2FS_I(inode); + struct inmem_pages *cur, *tmp; + bool submit_bio = false; + struct f2fs_io_info fio = { + .type = DATA, + .rw = WRITE_SYNC | REQ_PRIO, + }; + + /* + * The abort is true only when f2fs_evict_inode is called. + * Basically, the f2fs_evict_inode doesn't produce any data writes, so + * that we don't need to call f2fs_balance_fs. + * Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this + * inode becomes free by iget_locked in f2fs_iget. + */ + if (!abort) { + f2fs_balance_fs(sbi); + f2fs_lock_op(sbi); + } + + mutex_lock(&fi->inmem_lock); + list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { + if (!abort) { + lock_page(cur->page); + if (cur->page->mapping == inode->i_mapping) { + f2fs_wait_on_page_writeback(cur->page, DATA); + if (clear_page_dirty_for_io(cur->page)) + inode_dec_dirty_pages(inode); + trace_f2fs_commit_inmem_page(cur->page, INMEM); + do_write_data_page(cur->page, &fio); + submit_bio = true; + } + f2fs_put_page(cur->page, 1); + } else { + trace_f2fs_commit_inmem_page(cur->page, INMEM_DROP); + put_page(cur->page); + } + radix_tree_delete(&fi->inmem_root, cur->page->index); + list_del(&cur->list); + kmem_cache_free(inmem_entry_slab, cur); + dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); + } + mutex_unlock(&fi->inmem_lock); + + if (!abort) { + f2fs_unlock_op(sbi); + if (submit_bio) + f2fs_submit_merged_bio(sbi, DATA, WRITE); + } +} + +/* + * This function balances dirty node and dentry pages. + * In addition, it controls garbage collection. + */ +void f2fs_balance_fs(struct f2fs_sb_info *sbi) +{ + /* + * We should do GC or end up with checkpoint, if there are so many dirty + * dir/node pages without enough free segments. + */ + if (has_not_enough_free_secs(sbi, 0)) { + mutex_lock(&sbi->gc_mutex); + f2fs_gc(sbi); + } +} + +void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) +{ + /* try to shrink extent cache when there is no enough memory */ + f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER); + + /* check the # of cached NAT entries and prefree segments */ + if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) || + excess_prefree_segs(sbi) || + !available_free_memory(sbi, INO_ENTRIES)) + f2fs_sync_fs(sbi->sb, true); +} + +struct __submit_bio_ret { + struct completion event; + int error; +}; + +static void __submit_bio_wait_endio(struct bio *bio, int error) +{ + struct __submit_bio_ret *ret = bio->bi_private; + + ret->error = error; + complete(&ret->event); +} + +static int __submit_bio_wait(int rw, struct bio *bio) +{ + struct __submit_bio_ret ret; + + rw |= REQ_SYNC; + init_completion(&ret.event); + bio->bi_private = &ret; + bio->bi_end_io = __submit_bio_wait_endio; + submit_bio(rw, bio); + wait_for_completion(&ret.event); + + return ret.error; +} + +static int issue_flush_thread(void *data) +{ + struct f2fs_sb_info *sbi = data; + struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + wait_queue_head_t *q = &fcc->flush_wait_queue; +repeat: + if (kthread_should_stop()) + return 0; + + if (!llist_empty(&fcc->issue_list)) { + struct bio *bio = bio_alloc(GFP_NOIO, 0); + struct flush_cmd *cmd, *next; + int ret; + + fcc->dispatch_list = llist_del_all(&fcc->issue_list); + fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); + + bio->bi_bdev = sbi->sb->s_bdev; + ret = __submit_bio_wait(WRITE_FLUSH, bio); + + llist_for_each_entry_safe(cmd, next, + fcc->dispatch_list, llnode) { + cmd->ret = ret; + complete(&cmd->wait); + } + bio_put(bio); + fcc->dispatch_list = NULL; + } + + wait_event_interruptible(*q, + kthread_should_stop() || !llist_empty(&fcc->issue_list)); + goto repeat; +} + +int f2fs_issue_flush(struct f2fs_sb_info *sbi) +{ + struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + struct flush_cmd cmd; + + trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER), + test_opt(sbi, FLUSH_MERGE)); + + if (test_opt(sbi, NOBARRIER)) + return 0; + + if (!test_opt(sbi, FLUSH_MERGE)) + return blkdev_issue_flush(sbi->sb->s_bdev, GFP_KERNEL, NULL); + + init_completion(&cmd.wait); + + llist_add(&cmd.llnode, &fcc->issue_list); + + if (!fcc->dispatch_list) + wake_up(&fcc->flush_wait_queue); + + wait_for_completion(&cmd.wait); + + return cmd.ret; +} + +int create_flush_cmd_control(struct f2fs_sb_info *sbi) +{ + dev_t dev = sbi->sb->s_bdev->bd_dev; + struct flush_cmd_control *fcc; + int err = 0; + + fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); + if (!fcc) + return -ENOMEM; + init_waitqueue_head(&fcc->flush_wait_queue); + init_llist_head(&fcc->issue_list); + SM_I(sbi)->cmd_control_info = fcc; + fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, + "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); + if (IS_ERR(fcc->f2fs_issue_flush)) { + err = PTR_ERR(fcc->f2fs_issue_flush); + kfree(fcc); + SM_I(sbi)->cmd_control_info = NULL; + return err; + } + + return err; +} + +void destroy_flush_cmd_control(struct f2fs_sb_info *sbi) +{ + struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; + + if (fcc && fcc->f2fs_issue_flush) + kthread_stop(fcc->f2fs_issue_flush); + kfree(fcc); + SM_I(sbi)->cmd_control_info = NULL; +} + +static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, + enum dirty_type dirty_type) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + + /* need not be added */ + if (IS_CURSEG(sbi, segno)) + return; + + if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) + dirty_i->nr_dirty[dirty_type]++; + + if (dirty_type == DIRTY) { + struct seg_entry *sentry = get_seg_entry(sbi, segno); + enum dirty_type t = sentry->type; + + if (unlikely(t >= DIRTY)) { + f2fs_bug_on(sbi, 1); + return; + } + if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t])) + dirty_i->nr_dirty[t]++; + } +} + +static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, + enum dirty_type dirty_type) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + + if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type])) + dirty_i->nr_dirty[dirty_type]--; + + if (dirty_type == DIRTY) { + struct seg_entry *sentry = get_seg_entry(sbi, segno); + enum dirty_type t = sentry->type; + + if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) + dirty_i->nr_dirty[t]--; + + if (get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) + clear_bit(GET_SECNO(sbi, segno), + dirty_i->victim_secmap); + } +} + +/* + * Should not occur error such as -ENOMEM. + * Adding dirty entry into seglist is not critical operation. + * If a given segment is one of current working segments, it won't be added. + */ +static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + unsigned short valid_blocks; + + if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno)) + return; + + mutex_lock(&dirty_i->seglist_lock); + + valid_blocks = get_valid_blocks(sbi, segno, 0); + + if (valid_blocks == 0) { + __locate_dirty_segment(sbi, segno, PRE); + __remove_dirty_segment(sbi, segno, DIRTY); + } else if (valid_blocks < sbi->blocks_per_seg) { + __locate_dirty_segment(sbi, segno, DIRTY); + } else { + /* Recovery routine with SSR needs this */ + __remove_dirty_segment(sbi, segno, DIRTY); + } + + mutex_unlock(&dirty_i->seglist_lock); +} + +static int f2fs_issue_discard(struct f2fs_sb_info *sbi, + block_t blkstart, block_t blklen) +{ + sector_t start = SECTOR_FROM_BLOCK(blkstart); + sector_t len = SECTOR_FROM_BLOCK(blklen); + trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); +} + +void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr) +{ + if (f2fs_issue_discard(sbi, blkaddr, 1)) { + struct page *page = grab_meta_page(sbi, blkaddr); + /* zero-filled page */ + set_page_dirty(page); + f2fs_put_page(page, 1); + } +} + +static void __add_discard_entry(struct f2fs_sb_info *sbi, + struct cp_control *cpc, unsigned int start, unsigned int end) +{ + struct list_head *head = &SM_I(sbi)->discard_list; + struct discard_entry *new, *last; + + if (!list_empty(head)) { + last = list_last_entry(head, struct discard_entry, list); + if (START_BLOCK(sbi, cpc->trim_start) + start == + last->blkaddr + last->len) { + last->len += end - start; + goto done; + } + } + + new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); + INIT_LIST_HEAD(&new->list); + new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; + new->len = end - start; + list_add_tail(&new->list, head); +done: + SM_I(sbi)->nr_discards += end - start; + cpc->trimmed += end - start; +} + +static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); + int max_blocks = sbi->blocks_per_seg; + struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); + unsigned long *cur_map = (unsigned long *)se->cur_valid_map; + unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; + unsigned long *dmap = SIT_I(sbi)->tmp_map; + unsigned int start = 0, end = -1; + bool force = (cpc->reason == CP_DISCARD); + int i; + + if (!force && (!test_opt(sbi, DISCARD) || + SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards)) + return; + + if (force && !se->valid_blocks) { + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + /* + * if this segment is registered in the prefree list, then + * we should skip adding a discard candidate, and let the + * checkpoint do that later. + */ + mutex_lock(&dirty_i->seglist_lock); + if (test_bit(cpc->trim_start, dirty_i->dirty_segmap[PRE])) { + mutex_unlock(&dirty_i->seglist_lock); + cpc->trimmed += sbi->blocks_per_seg; + return; + } + mutex_unlock(&dirty_i->seglist_lock); + + __add_discard_entry(sbi, cpc, 0, sbi->blocks_per_seg); + return; + } + + /* zero block will be discarded through the prefree list */ + if (!se->valid_blocks || se->valid_blocks == max_blocks) + return; + + /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ + for (i = 0; i < entries; i++) + dmap[i] = force ? ~ckpt_map[i] : + (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; + + while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { + start = __find_rev_next_bit(dmap, max_blocks, end + 1); + if (start >= max_blocks) + break; + + end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); + + if (force && end - start < cpc->trim_minlen) + continue; + + __add_discard_entry(sbi, cpc, start, end); + } +} + +void release_discard_addrs(struct f2fs_sb_info *sbi) +{ + struct list_head *head = &(SM_I(sbi)->discard_list); + struct discard_entry *entry, *this; + + /* drop caches */ + list_for_each_entry_safe(entry, this, head, list) { + list_del(&entry->list); + kmem_cache_free(discard_entry_slab, entry); + } +} + +/* + * Should call clear_prefree_segments after checkpoint is done. + */ +static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + unsigned int segno; + + mutex_lock(&dirty_i->seglist_lock); + for_each_set_bit(segno, dirty_i->dirty_segmap[PRE], MAIN_SEGS(sbi)) + __set_test_and_free(sbi, segno); + mutex_unlock(&dirty_i->seglist_lock); +} + +void clear_prefree_segments(struct f2fs_sb_info *sbi) +{ + struct list_head *head = &(SM_I(sbi)->discard_list); + struct discard_entry *entry, *this; + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; + unsigned int start = 0, end = -1; + + mutex_lock(&dirty_i->seglist_lock); + + while (1) { + int i; + start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1); + if (start >= MAIN_SEGS(sbi)) + break; + end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi), + start + 1); + + for (i = start; i < end; i++) + clear_bit(i, prefree_map); + + dirty_i->nr_dirty[PRE] -= end - start; + + if (!test_opt(sbi, DISCARD)) + continue; + + f2fs_issue_discard(sbi, START_BLOCK(sbi, start), + (end - start) << sbi->log_blocks_per_seg); + } + mutex_unlock(&dirty_i->seglist_lock); + + /* send small discards */ + list_for_each_entry_safe(entry, this, head, list) { + f2fs_issue_discard(sbi, entry->blkaddr, entry->len); + list_del(&entry->list); + SM_I(sbi)->nr_discards -= entry->len; + kmem_cache_free(discard_entry_slab, entry); + } +} + +static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) +{ + struct sit_info *sit_i = SIT_I(sbi); + + if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) { + sit_i->dirty_sentries++; + return false; + } + + return true; +} + +static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type, + unsigned int segno, int modified) +{ + struct seg_entry *se = get_seg_entry(sbi, segno); + se->type = type; + if (modified) + __mark_sit_entry_dirty(sbi, segno); +} + +static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) +{ + struct seg_entry *se; + unsigned int segno, offset; + long int new_vblocks; + + segno = GET_SEGNO(sbi, blkaddr); + + se = get_seg_entry(sbi, segno); + new_vblocks = se->valid_blocks + del; + offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); + + f2fs_bug_on(sbi, (new_vblocks >> (sizeof(unsigned short) << 3) || + (new_vblocks > sbi->blocks_per_seg))); + + se->valid_blocks = new_vblocks; + se->mtime = get_mtime(sbi); + SIT_I(sbi)->max_mtime = se->mtime; + + /* Update valid block bitmap */ + if (del > 0) { + if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) + f2fs_bug_on(sbi, 1); + } else { + if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) + f2fs_bug_on(sbi, 1); + } + if (!f2fs_test_bit(offset, se->ckpt_valid_map)) + se->ckpt_valid_blocks += del; + + __mark_sit_entry_dirty(sbi, segno); + + /* update total number of valid blocks to be written in ckpt area */ + SIT_I(sbi)->written_valid_blocks += del; + + if (sbi->segs_per_sec > 1) + get_sec_entry(sbi, segno)->valid_blocks += del; +} + +void refresh_sit_entry(struct f2fs_sb_info *sbi, block_t old, block_t new) +{ + update_sit_entry(sbi, new, 1); + if (GET_SEGNO(sbi, old) != NULL_SEGNO) + update_sit_entry(sbi, old, -1); + + locate_dirty_segment(sbi, GET_SEGNO(sbi, old)); + locate_dirty_segment(sbi, GET_SEGNO(sbi, new)); +} + +void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) +{ + unsigned int segno = GET_SEGNO(sbi, addr); + struct sit_info *sit_i = SIT_I(sbi); + + f2fs_bug_on(sbi, addr == NULL_ADDR); + if (addr == NEW_ADDR) + return; + + /* add it into sit main buffer */ + mutex_lock(&sit_i->sentry_lock); + + update_sit_entry(sbi, addr, -1); + + /* add it into dirty seglist */ + locate_dirty_segment(sbi, segno); + + mutex_unlock(&sit_i->sentry_lock); +} + +/* + * This function should be resided under the curseg_mutex lock + */ +static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, + struct f2fs_summary *sum) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + void *addr = curseg->sum_blk; + addr += curseg->next_blkoff * sizeof(struct f2fs_summary); + memcpy(addr, sum, sizeof(struct f2fs_summary)); +} + +/* + * Calculate the number of current summary pages for writing + */ +int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) +{ + int valid_sum_count = 0; + int i, sum_in_page; + + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + if (sbi->ckpt->alloc_type[i] == SSR) + valid_sum_count += sbi->blocks_per_seg; + else { + if (for_ra) + valid_sum_count += le16_to_cpu( + F2FS_CKPT(sbi)->cur_data_blkoff[i]); + else + valid_sum_count += curseg_blkoff(sbi, i); + } + } + + sum_in_page = (PAGE_CACHE_SIZE - 2 * SUM_JOURNAL_SIZE - + SUM_FOOTER_SIZE) / SUMMARY_SIZE; + if (valid_sum_count <= sum_in_page) + return 1; + else if ((valid_sum_count - sum_in_page) <= + (PAGE_CACHE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) + return 2; + return 3; +} + +/* + * Caller should put this summary page + */ +struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) +{ + return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno)); +} + +static void write_sum_page(struct f2fs_sb_info *sbi, + struct f2fs_summary_block *sum_blk, block_t blk_addr) +{ + struct page *page = grab_meta_page(sbi, blk_addr); + void *kaddr = page_address(page); + memcpy(kaddr, sum_blk, PAGE_CACHE_SIZE); + set_page_dirty(page); + f2fs_put_page(page, 1); +} + +static int is_next_segment_free(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int segno = curseg->segno + 1; + struct free_segmap_info *free_i = FREE_I(sbi); + + if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) + return !test_bit(segno, free_i->free_segmap); + return 0; +} + +/* + * Find a new segment from the free segments bitmap to right order + * This function should be returned with success, otherwise BUG + */ +static void get_new_segment(struct f2fs_sb_info *sbi, + unsigned int *newseg, bool new_sec, int dir) +{ + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int segno, secno, zoneno; + unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone; + unsigned int hint = *newseg / sbi->segs_per_sec; + unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg); + unsigned int left_start = hint; + bool init = true; + int go_left = 0; + int i; + + spin_lock(&free_i->segmap_lock); + + if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { + segno = find_next_zero_bit(free_i->free_segmap, + MAIN_SEGS(sbi), *newseg + 1); + if (segno - *newseg < sbi->segs_per_sec - + (*newseg % sbi->segs_per_sec)) + goto got_it; + } +find_other_zone: + secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint); + if (secno >= MAIN_SECS(sbi)) { + if (dir == ALLOC_RIGHT) { + secno = find_next_zero_bit(free_i->free_secmap, + MAIN_SECS(sbi), 0); + f2fs_bug_on(sbi, secno >= MAIN_SECS(sbi)); + } else { + go_left = 1; + left_start = hint - 1; + } + } + if (go_left == 0) + goto skip_left; + + while (test_bit(left_start, free_i->free_secmap)) { + if (left_start > 0) { + left_start--; + continue; + } + left_start = find_next_zero_bit(free_i->free_secmap, + MAIN_SECS(sbi), 0); + f2fs_bug_on(sbi, left_start >= MAIN_SECS(sbi)); + break; + } + secno = left_start; +skip_left: + hint = secno; + segno = secno * sbi->segs_per_sec; + zoneno = secno / sbi->secs_per_zone; + + /* give up on finding another zone */ + if (!init) + goto got_it; + if (sbi->secs_per_zone == 1) + goto got_it; + if (zoneno == old_zoneno) + goto got_it; + if (dir == ALLOC_LEFT) { + if (!go_left && zoneno + 1 >= total_zones) + goto got_it; + if (go_left && zoneno == 0) + goto got_it; + } + for (i = 0; i < NR_CURSEG_TYPE; i++) + if (CURSEG_I(sbi, i)->zone == zoneno) + break; + + if (i < NR_CURSEG_TYPE) { + /* zone is in user, try another */ + if (go_left) + hint = zoneno * sbi->secs_per_zone - 1; + else if (zoneno + 1 >= total_zones) + hint = 0; + else + hint = (zoneno + 1) * sbi->secs_per_zone; + init = false; + goto find_other_zone; + } +got_it: + /* set it as dirty segment in free segmap */ + f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); + __set_inuse(sbi, segno); + *newseg = segno; + spin_unlock(&free_i->segmap_lock); +} + +static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + struct summary_footer *sum_footer; + + curseg->segno = curseg->next_segno; + curseg->zone = GET_ZONENO_FROM_SEGNO(sbi, curseg->segno); + curseg->next_blkoff = 0; + curseg->next_segno = NULL_SEGNO; + + sum_footer = &(curseg->sum_blk->footer); + memset(sum_footer, 0, sizeof(struct summary_footer)); + if (IS_DATASEG(type)) + SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); + if (IS_NODESEG(type)) + SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); + __set_sit_entry_type(sbi, type, curseg->segno, modified); +} + +/* + * Allocate a current working segment. + * This function always allocates a free segment in LFS manner. + */ +static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int segno = curseg->segno; + int dir = ALLOC_LEFT; + + write_sum_page(sbi, curseg->sum_blk, + GET_SUM_BLOCK(sbi, segno)); + if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA) + dir = ALLOC_RIGHT; + + if (test_opt(sbi, NOHEAP)) + dir = ALLOC_RIGHT; + + get_new_segment(sbi, &segno, new_sec, dir); + curseg->next_segno = segno; + reset_curseg(sbi, type, 1); + curseg->alloc_type = LFS; +} + +static void __next_free_blkoff(struct f2fs_sb_info *sbi, + struct curseg_info *seg, block_t start) +{ + struct seg_entry *se = get_seg_entry(sbi, seg->segno); + int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); + unsigned long *target_map = SIT_I(sbi)->tmp_map; + unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; + unsigned long *cur_map = (unsigned long *)se->cur_valid_map; + int i, pos; + + for (i = 0; i < entries; i++) + target_map[i] = ckpt_map[i] | cur_map[i]; + + pos = __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start); + + seg->next_blkoff = pos; +} + +/* + * If a segment is written by LFS manner, next block offset is just obtained + * by increasing the current block offset. However, if a segment is written by + * SSR manner, next block offset obtained by calling __next_free_blkoff + */ +static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, + struct curseg_info *seg) +{ + if (seg->alloc_type == SSR) + __next_free_blkoff(sbi, seg, seg->next_blkoff + 1); + else + seg->next_blkoff++; +} + +/* + * This function always allocates a used segment(from dirty seglist) by SSR + * manner, so it should recover the existing segment information of valid blocks + */ +static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int new_segno = curseg->next_segno; + struct f2fs_summary_block *sum_node; + struct page *sum_page; + + write_sum_page(sbi, curseg->sum_blk, + GET_SUM_BLOCK(sbi, curseg->segno)); + __set_test_and_inuse(sbi, new_segno); + + mutex_lock(&dirty_i->seglist_lock); + __remove_dirty_segment(sbi, new_segno, PRE); + __remove_dirty_segment(sbi, new_segno, DIRTY); + mutex_unlock(&dirty_i->seglist_lock); + + reset_curseg(sbi, type, 1); + curseg->alloc_type = SSR; + __next_free_blkoff(sbi, curseg, 0); + + if (reuse) { + sum_page = get_sum_page(sbi, new_segno); + sum_node = (struct f2fs_summary_block *)page_address(sum_page); + memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); + f2fs_put_page(sum_page, 1); + } +} + +static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + + if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0)) + return v_ops->get_victim(sbi, + &(curseg)->next_segno, BG_GC, type, SSR); + + /* For data segments, let's do SSR more intensively */ + for (; type >= CURSEG_HOT_DATA; type--) + if (v_ops->get_victim(sbi, &(curseg)->next_segno, + BG_GC, type, SSR)) + return 1; + return 0; +} + +/* + * flush out current segment and replace it with new segment + * This function should be returned with success, otherwise BUG + */ +static void allocate_segment_by_default(struct f2fs_sb_info *sbi, + int type, bool force) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + + if (force) + new_curseg(sbi, type, true); + else if (type == CURSEG_WARM_NODE) + new_curseg(sbi, type, false); + else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type)) + new_curseg(sbi, type, false); + else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) + change_curseg(sbi, type, true); + else + new_curseg(sbi, type, false); + + stat_inc_seg_type(sbi, curseg); +} + +static void __allocate_new_segments(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + unsigned int old_segno; + + old_segno = curseg->segno; + SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); + locate_dirty_segment(sbi, old_segno); +} + +void allocate_new_segments(struct f2fs_sb_info *sbi) +{ + int i; + + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) + __allocate_new_segments(sbi, i); +} + +static const struct segment_allocation default_salloc_ops = { + .allocate_segment = allocate_segment_by_default, +}; + +int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) +{ + __u64 start = F2FS_BYTES_TO_BLK(range->start); + __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; + unsigned int start_segno, end_segno; + struct cp_control cpc; + + if (range->minlen > SEGMENT_SIZE(sbi) || start >= MAX_BLKADDR(sbi) || + range->len < sbi->blocksize) + return -EINVAL; + + cpc.trimmed = 0; + if (end <= MAIN_BLKADDR(sbi)) + goto out; + + /* start/end segment number in main_area */ + start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); + end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : + GET_SEGNO(sbi, end); + cpc.reason = CP_DISCARD; + cpc.trim_minlen = F2FS_BYTES_TO_BLK(range->minlen); + + /* do checkpoint to issue discard commands safely */ + for (; start_segno <= end_segno; start_segno = cpc.trim_end + 1) { + cpc.trim_start = start_segno; + cpc.trim_end = min_t(unsigned int, rounddown(start_segno + + BATCHED_TRIM_SEGMENTS(sbi), + sbi->segs_per_sec) - 1, end_segno); + + mutex_lock(&sbi->gc_mutex); + write_checkpoint(sbi, &cpc); + mutex_unlock(&sbi->gc_mutex); + } +out: + range->len = F2FS_BLK_TO_BYTES(cpc.trimmed); + return 0; +} + +static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + if (curseg->next_blkoff < sbi->blocks_per_seg) + return true; + return false; +} + +static int __get_segment_type_2(struct page *page, enum page_type p_type) +{ + if (p_type == DATA) + return CURSEG_HOT_DATA; + else + return CURSEG_HOT_NODE; +} + +static int __get_segment_type_4(struct page *page, enum page_type p_type) +{ + if (p_type == DATA) { + struct inode *inode = page->mapping->host; + + if (S_ISDIR(inode->i_mode)) + return CURSEG_HOT_DATA; + else + return CURSEG_COLD_DATA; + } else { + if (IS_DNODE(page) && is_cold_node(page)) + return CURSEG_WARM_NODE; + else + return CURSEG_COLD_NODE; + } +} + +static int __get_segment_type_6(struct page *page, enum page_type p_type) +{ + if (p_type == DATA) { + struct inode *inode = page->mapping->host; + + if (S_ISDIR(inode->i_mode)) + return CURSEG_HOT_DATA; + else if (is_cold_data(page) || file_is_cold(inode)) + return CURSEG_COLD_DATA; + else + return CURSEG_WARM_DATA; + } else { + if (IS_DNODE(page)) + return is_cold_node(page) ? CURSEG_WARM_NODE : + CURSEG_HOT_NODE; + else + return CURSEG_COLD_NODE; + } +} + +static int __get_segment_type(struct page *page, enum page_type p_type) +{ + switch (F2FS_P_SB(page)->active_logs) { + case 2: + return __get_segment_type_2(page, p_type); + case 4: + return __get_segment_type_4(page, p_type); + } + /* NR_CURSEG_TYPE(6) logs by default */ + f2fs_bug_on(F2FS_P_SB(page), + F2FS_P_SB(page)->active_logs != NR_CURSEG_TYPE); + return __get_segment_type_6(page, p_type); +} + +void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, + block_t old_blkaddr, block_t *new_blkaddr, + struct f2fs_summary *sum, int type) +{ + struct sit_info *sit_i = SIT_I(sbi); + struct curseg_info *curseg; + bool direct_io = (type == CURSEG_DIRECT_IO); + + type = direct_io ? CURSEG_WARM_DATA : type; + + curseg = CURSEG_I(sbi, type); + + mutex_lock(&curseg->curseg_mutex); + mutex_lock(&sit_i->sentry_lock); + + /* direct_io'ed data is aligned to the segment for better performance */ + if (direct_io && curseg->next_blkoff) + __allocate_new_segments(sbi, type); + + *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); + + /* + * __add_sum_entry should be resided under the curseg_mutex + * because, this function updates a summary entry in the + * current summary block. + */ + __add_sum_entry(sbi, type, sum); + + __refresh_next_blkoff(sbi, curseg); + + stat_inc_block_count(sbi, curseg); + + if (!__has_curseg_space(sbi, type)) + sit_i->s_ops->allocate_segment(sbi, type, false); + /* + * SIT information should be updated before segment allocation, + * since SSR needs latest valid block information. + */ + refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); + + mutex_unlock(&sit_i->sentry_lock); + + if (page && IS_NODESEG(type)) + fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); + + mutex_unlock(&curseg->curseg_mutex); +} + +static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, + struct f2fs_summary *sum, + struct f2fs_io_info *fio) +{ + int type = __get_segment_type(page, fio->type); + + allocate_data_block(sbi, page, fio->blk_addr, &fio->blk_addr, sum, type); + + /* writeout dirty page into bdev */ + f2fs_submit_page_mbio(sbi, page, fio); +} + +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) +{ + struct f2fs_io_info fio = { + .type = META, + .rw = WRITE_SYNC | REQ_META | REQ_PRIO, + .blk_addr = page->index, + }; + + set_page_writeback(page); + f2fs_submit_page_mbio(sbi, page, &fio); +} + +void write_node_page(struct f2fs_sb_info *sbi, struct page *page, + unsigned int nid, struct f2fs_io_info *fio) +{ + struct f2fs_summary sum; + set_summary(&sum, nid, 0, 0); + do_write_page(sbi, page, &sum, fio); +} + +void write_data_page(struct page *page, struct dnode_of_data *dn, + struct f2fs_io_info *fio) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode); + struct f2fs_summary sum; + struct node_info ni; + + f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); + get_node_info(sbi, dn->nid, &ni); + set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); + do_write_page(sbi, page, &sum, fio); + dn->data_blkaddr = fio->blk_addr; +} + +void rewrite_data_page(struct page *page, struct f2fs_io_info *fio) +{ + stat_inc_inplace_blocks(F2FS_P_SB(page)); + f2fs_submit_page_mbio(F2FS_P_SB(page), page, fio); +} + +void recover_data_page(struct f2fs_sb_info *sbi, + struct page *page, struct f2fs_summary *sum, + block_t old_blkaddr, block_t new_blkaddr) +{ + struct sit_info *sit_i = SIT_I(sbi); + struct curseg_info *curseg; + unsigned int segno, old_cursegno; + struct seg_entry *se; + int type; + + segno = GET_SEGNO(sbi, new_blkaddr); + se = get_seg_entry(sbi, segno); + type = se->type; + + if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { + if (old_blkaddr == NULL_ADDR) + type = CURSEG_COLD_DATA; + else + type = CURSEG_WARM_DATA; + } + curseg = CURSEG_I(sbi, type); + + mutex_lock(&curseg->curseg_mutex); + mutex_lock(&sit_i->sentry_lock); + + old_cursegno = curseg->segno; + + /* change the current segment */ + if (segno != curseg->segno) { + curseg->next_segno = segno; + change_curseg(sbi, type, true); + } + + curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); + __add_sum_entry(sbi, type, sum); + + refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); + locate_dirty_segment(sbi, old_cursegno); + + mutex_unlock(&sit_i->sentry_lock); + mutex_unlock(&curseg->curseg_mutex); +} + +static inline bool is_merged_page(struct f2fs_sb_info *sbi, + struct page *page, enum page_type type) +{ + enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct f2fs_bio_info *io = &sbi->write_io[btype]; + struct bio_vec *bvec; + int i; + + down_read(&io->io_rwsem); + if (!io->bio) + goto out; + + __bio_for_each_segment(bvec, io->bio, i, 0) { + if (page == bvec->bv_page) { + up_read(&io->io_rwsem); + return true; + } + } + +out: + up_read(&io->io_rwsem); + return false; +} + +void f2fs_wait_on_page_writeback(struct page *page, + enum page_type type) +{ + if (PageWriteback(page)) { + struct f2fs_sb_info *sbi = F2FS_P_SB(page); + + if (is_merged_page(sbi, page, type)) + f2fs_submit_merged_bio(sbi, type, WRITE); + wait_on_page_writeback(page); + } +} + +static int read_compacted_summaries(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct curseg_info *seg_i; + unsigned char *kaddr; + struct page *page; + block_t start; + int i, j, offset; + + start = start_sum_block(sbi); + + page = get_meta_page(sbi, start++); + kaddr = (unsigned char *)page_address(page); + + /* Step 1: restore nat cache */ + seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); + memcpy(&seg_i->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE); + + /* Step 2: restore sit cache */ + seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); + memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE, + SUM_JOURNAL_SIZE); + offset = 2 * SUM_JOURNAL_SIZE; + + /* Step 3: restore summary entries */ + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + unsigned short blk_off; + unsigned int segno; + + seg_i = CURSEG_I(sbi, i); + segno = le32_to_cpu(ckpt->cur_data_segno[i]); + blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); + seg_i->next_segno = segno; + reset_curseg(sbi, i, 0); + seg_i->alloc_type = ckpt->alloc_type[i]; + seg_i->next_blkoff = blk_off; + + if (seg_i->alloc_type == SSR) + blk_off = sbi->blocks_per_seg; + + for (j = 0; j < blk_off; j++) { + struct f2fs_summary *s; + s = (struct f2fs_summary *)(kaddr + offset); + seg_i->sum_blk->entries[j] = *s; + offset += SUMMARY_SIZE; + if (offset + SUMMARY_SIZE <= PAGE_CACHE_SIZE - + SUM_FOOTER_SIZE) + continue; + + f2fs_put_page(page, 1); + page = NULL; + + page = get_meta_page(sbi, start++); + kaddr = (unsigned char *)page_address(page); + offset = 0; + } + } + f2fs_put_page(page, 1); + return 0; +} + +static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) +{ + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_summary_block *sum; + struct curseg_info *curseg; + struct page *new; + unsigned short blk_off; + unsigned int segno = 0; + block_t blk_addr = 0; + + /* get segment number and block addr */ + if (IS_DATASEG(type)) { + segno = le32_to_cpu(ckpt->cur_data_segno[type]); + blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - + CURSEG_HOT_DATA]); + if (__exist_node_summaries(sbi)) + blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type); + else + blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); + } else { + segno = le32_to_cpu(ckpt->cur_node_segno[type - + CURSEG_HOT_NODE]); + blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - + CURSEG_HOT_NODE]); + if (__exist_node_summaries(sbi)) + blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, + type - CURSEG_HOT_NODE); + else + blk_addr = GET_SUM_BLOCK(sbi, segno); + } + + new = get_meta_page(sbi, blk_addr); + sum = (struct f2fs_summary_block *)page_address(new); + + if (IS_NODESEG(type)) { + if (__exist_node_summaries(sbi)) { + struct f2fs_summary *ns = &sum->entries[0]; + int i; + for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { + ns->version = 0; + ns->ofs_in_node = 0; + } + } else { + int err; + + err = restore_node_summary(sbi, segno, sum); + if (err) { + f2fs_put_page(new, 1); + return err; + } + } + } + + /* set uncompleted segment to curseg */ + curseg = CURSEG_I(sbi, type); + mutex_lock(&curseg->curseg_mutex); + memcpy(curseg->sum_blk, sum, PAGE_CACHE_SIZE); + curseg->next_segno = segno; + reset_curseg(sbi, type, 0); + curseg->alloc_type = ckpt->alloc_type[type]; + curseg->next_blkoff = blk_off; + mutex_unlock(&curseg->curseg_mutex); + f2fs_put_page(new, 1); + return 0; +} + +static int restore_curseg_summaries(struct f2fs_sb_info *sbi) +{ + int type = CURSEG_HOT_DATA; + int err; + + if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { + int npages = npages_for_summary_flush(sbi, true); + + if (npages >= 2) + ra_meta_pages(sbi, start_sum_block(sbi), npages, + META_CP); + + /* restore for compacted data summary */ + if (read_compacted_summaries(sbi)) + return -EINVAL; + type = CURSEG_HOT_NODE; + } + + if (__exist_node_summaries(sbi)) + ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type), + NR_CURSEG_TYPE - type, META_CP); + + for (; type <= CURSEG_COLD_NODE; type++) { + err = read_normal_summaries(sbi, type); + if (err) + return err; + } + + return 0; +} + +static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) +{ + struct page *page; + unsigned char *kaddr; + struct f2fs_summary *summary; + struct curseg_info *seg_i; + int written_size = 0; + int i, j; + + page = grab_meta_page(sbi, blkaddr++); + kaddr = (unsigned char *)page_address(page); + + /* Step 1: write nat cache */ + seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); + memcpy(kaddr, &seg_i->sum_blk->n_nats, SUM_JOURNAL_SIZE); + written_size += SUM_JOURNAL_SIZE; + + /* Step 2: write sit cache */ + seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); + memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits, + SUM_JOURNAL_SIZE); + written_size += SUM_JOURNAL_SIZE; + + /* Step 3: write summary entries */ + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { + unsigned short blkoff; + seg_i = CURSEG_I(sbi, i); + if (sbi->ckpt->alloc_type[i] == SSR) + blkoff = sbi->blocks_per_seg; + else + blkoff = curseg_blkoff(sbi, i); + + for (j = 0; j < blkoff; j++) { + if (!page) { + page = grab_meta_page(sbi, blkaddr++); + kaddr = (unsigned char *)page_address(page); + written_size = 0; + } + summary = (struct f2fs_summary *)(kaddr + written_size); + *summary = seg_i->sum_blk->entries[j]; + written_size += SUMMARY_SIZE; + + if (written_size + SUMMARY_SIZE <= PAGE_CACHE_SIZE - + SUM_FOOTER_SIZE) + continue; + + set_page_dirty(page); + f2fs_put_page(page, 1); + page = NULL; + } + } + if (page) { + set_page_dirty(page); + f2fs_put_page(page, 1); + } +} + +static void write_normal_summaries(struct f2fs_sb_info *sbi, + block_t blkaddr, int type) +{ + int i, end; + if (IS_DATASEG(type)) + end = type + NR_CURSEG_DATA_TYPE; + else + end = type + NR_CURSEG_NODE_TYPE; + + for (i = type; i < end; i++) { + struct curseg_info *sum = CURSEG_I(sbi, i); + mutex_lock(&sum->curseg_mutex); + write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type)); + mutex_unlock(&sum->curseg_mutex); + } +} + +void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) +{ + if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) + write_compacted_summaries(sbi, start_blk); + else + write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); +} + +void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) +{ + write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); +} + +int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, + unsigned int val, int alloc) +{ + int i; + + if (type == NAT_JOURNAL) { + for (i = 0; i < nats_in_cursum(sum); i++) { + if (le32_to_cpu(nid_in_journal(sum, i)) == val) + return i; + } + if (alloc && nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES) + return update_nats_in_cursum(sum, 1); + } else if (type == SIT_JOURNAL) { + for (i = 0; i < sits_in_cursum(sum); i++) + if (le32_to_cpu(segno_in_journal(sum, i)) == val) + return i; + if (alloc && sits_in_cursum(sum) < SIT_JOURNAL_ENTRIES) + return update_sits_in_cursum(sum, 1); + } + return -1; +} + +static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + return get_meta_page(sbi, current_sit_addr(sbi, segno)); +} + +static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, + unsigned int start) +{ + struct sit_info *sit_i = SIT_I(sbi); + struct page *src_page, *dst_page; + pgoff_t src_off, dst_off; + void *src_addr, *dst_addr; + + src_off = current_sit_addr(sbi, start); + dst_off = next_sit_addr(sbi, src_off); + + /* get current sit block page without lock */ + src_page = get_meta_page(sbi, src_off); + dst_page = grab_meta_page(sbi, dst_off); + f2fs_bug_on(sbi, PageDirty(src_page)); + + src_addr = page_address(src_page); + dst_addr = page_address(dst_page); + memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); + + set_page_dirty(dst_page); + f2fs_put_page(src_page, 1); + + set_to_next_sit(sit_i, start); + + return dst_page; +} + +static struct sit_entry_set *grab_sit_entry_set(void) +{ + struct sit_entry_set *ses = + f2fs_kmem_cache_alloc(sit_entry_set_slab, GFP_ATOMIC); + + ses->entry_cnt = 0; + INIT_LIST_HEAD(&ses->set_list); + return ses; +} + +static void release_sit_entry_set(struct sit_entry_set *ses) +{ + list_del(&ses->set_list); + kmem_cache_free(sit_entry_set_slab, ses); +} + +static void adjust_sit_entry_set(struct sit_entry_set *ses, + struct list_head *head) +{ + struct sit_entry_set *next = ses; + + if (list_is_last(&ses->set_list, head)) + return; + + list_for_each_entry_continue(next, head, set_list) + if (ses->entry_cnt <= next->entry_cnt) + break; + + list_move_tail(&ses->set_list, &next->set_list); +} + +static void add_sit_entry(unsigned int segno, struct list_head *head) +{ + struct sit_entry_set *ses; + unsigned int start_segno = START_SEGNO(segno); + + list_for_each_entry(ses, head, set_list) { + if (ses->start_segno == start_segno) { + ses->entry_cnt++; + adjust_sit_entry_set(ses, head); + return; + } + } + + ses = grab_sit_entry_set(); + + ses->start_segno = start_segno; + ses->entry_cnt++; + list_add(&ses->set_list, head); +} + +static void add_sits_in_set(struct f2fs_sb_info *sbi) +{ + struct f2fs_sm_info *sm_info = SM_I(sbi); + struct list_head *set_list = &sm_info->sit_entry_set; + unsigned long *bitmap = SIT_I(sbi)->dirty_sentries_bitmap; + unsigned int segno; + + for_each_set_bit(segno, bitmap, MAIN_SEGS(sbi)) + add_sit_entry(segno, set_list); +} + +static void remove_sits_in_journal(struct f2fs_sb_info *sbi) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + int i; + + for (i = sits_in_cursum(sum) - 1; i >= 0; i--) { + unsigned int segno; + bool dirtied; + + segno = le32_to_cpu(segno_in_journal(sum, i)); + dirtied = __mark_sit_entry_dirty(sbi, segno); + + if (!dirtied) + add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); + } + update_sits_in_cursum(sum, -sits_in_cursum(sum)); +} + +/* + * CP calls this function, which flushes SIT entries including sit_journal, + * and moves prefree segs to free segs. + */ +void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned long *bitmap = sit_i->dirty_sentries_bitmap; + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct sit_entry_set *ses, *tmp; + struct list_head *head = &SM_I(sbi)->sit_entry_set; + bool to_journal = true; + struct seg_entry *se; + + mutex_lock(&curseg->curseg_mutex); + mutex_lock(&sit_i->sentry_lock); + + if (!sit_i->dirty_sentries) + goto out; + + /* + * add and account sit entries of dirty bitmap in sit entry + * set temporarily + */ + add_sits_in_set(sbi); + + /* + * if there are no enough space in journal to store dirty sit + * entries, remove all entries from journal and add and account + * them in sit entry set. + */ + if (!__has_cursum_space(sum, sit_i->dirty_sentries, SIT_JOURNAL)) + remove_sits_in_journal(sbi); + + /* + * there are two steps to flush sit entries: + * #1, flush sit entries to journal in current cold data summary block. + * #2, flush sit entries to sit page. + */ + list_for_each_entry_safe(ses, tmp, head, set_list) { + struct page *page = NULL; + struct f2fs_sit_block *raw_sit = NULL; + unsigned int start_segno = ses->start_segno; + unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, + (unsigned long)MAIN_SEGS(sbi)); + unsigned int segno = start_segno; + + if (to_journal && + !__has_cursum_space(sum, ses->entry_cnt, SIT_JOURNAL)) + to_journal = false; + + if (!to_journal) { + page = get_next_sit_page(sbi, start_segno); + raw_sit = page_address(page); + } + + /* flush dirty sit entries in region of current sit set */ + for_each_set_bit_from(segno, bitmap, end) { + int offset, sit_offset; + + se = get_seg_entry(sbi, segno); + + /* add discard candidates */ + if (cpc->reason != CP_DISCARD) { + cpc->trim_start = segno; + add_discard_addrs(sbi, cpc); + } + + if (to_journal) { + offset = lookup_journal_in_cursum(sum, + SIT_JOURNAL, segno, 1); + f2fs_bug_on(sbi, offset < 0); + segno_in_journal(sum, offset) = + cpu_to_le32(segno); + seg_info_to_raw_sit(se, + &sit_in_journal(sum, offset)); + } else { + sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); + seg_info_to_raw_sit(se, + &raw_sit->entries[sit_offset]); + } + + __clear_bit(segno, bitmap); + sit_i->dirty_sentries--; + ses->entry_cnt--; + } + + if (!to_journal) + f2fs_put_page(page, 1); + + f2fs_bug_on(sbi, ses->entry_cnt); + release_sit_entry_set(ses); + } + + f2fs_bug_on(sbi, !list_empty(head)); + f2fs_bug_on(sbi, sit_i->dirty_sentries); +out: + if (cpc->reason == CP_DISCARD) { + for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) + add_discard_addrs(sbi, cpc); + } + mutex_unlock(&sit_i->sentry_lock); + mutex_unlock(&curseg->curseg_mutex); + + set_prefree_as_free_segments(sbi); +} + +static int build_sit_info(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct sit_info *sit_i; + unsigned int sit_segs, start; + char *src_bitmap, *dst_bitmap; + unsigned int bitmap_size; + + /* allocate memory for SIT information */ + sit_i = kzalloc(sizeof(struct sit_info), GFP_KERNEL); + if (!sit_i) + return -ENOMEM; + + SM_I(sbi)->sit_info = sit_i; + + sit_i->sentries = vzalloc(MAIN_SEGS(sbi) * sizeof(struct seg_entry)); + if (!sit_i->sentries) + return -ENOMEM; + + bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); + sit_i->dirty_sentries_bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!sit_i->dirty_sentries_bitmap) + return -ENOMEM; + + for (start = 0; start < MAIN_SEGS(sbi); start++) { + sit_i->sentries[start].cur_valid_map + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + sit_i->sentries[start].ckpt_valid_map + = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->sentries[start].cur_valid_map + || !sit_i->sentries[start].ckpt_valid_map) + return -ENOMEM; + } + + sit_i->tmp_map = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); + if (!sit_i->tmp_map) + return -ENOMEM; + + if (sbi->segs_per_sec > 1) { + sit_i->sec_entries = vzalloc(MAIN_SECS(sbi) * + sizeof(struct sec_entry)); + if (!sit_i->sec_entries) + return -ENOMEM; + } + + /* get information related with SIT */ + sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1; + + /* setup SIT bitmap from ckeckpoint pack */ + bitmap_size = __bitmap_size(sbi, SIT_BITMAP); + src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); + + dst_bitmap = kmemdup(src_bitmap, bitmap_size, GFP_KERNEL); + if (!dst_bitmap) + return -ENOMEM; + + /* init SIT information */ + sit_i->s_ops = &default_salloc_ops; + + sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); + sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; + sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count); + sit_i->sit_bitmap = dst_bitmap; + sit_i->bitmap_size = bitmap_size; + sit_i->dirty_sentries = 0; + sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; + sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); + sit_i->mounted_time = CURRENT_TIME_SEC.tv_sec; + mutex_init(&sit_i->sentry_lock); + return 0; +} + +static int build_free_segmap(struct f2fs_sb_info *sbi) +{ + struct free_segmap_info *free_i; + unsigned int bitmap_size, sec_bitmap_size; + + /* allocate memory for free segmap information */ + free_i = kzalloc(sizeof(struct free_segmap_info), GFP_KERNEL); + if (!free_i) + return -ENOMEM; + + SM_I(sbi)->free_info = free_i; + + bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); + free_i->free_segmap = kmalloc(bitmap_size, GFP_KERNEL); + if (!free_i->free_segmap) + return -ENOMEM; + + sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); + free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL); + if (!free_i->free_secmap) + return -ENOMEM; + + /* set all segments as dirty temporarily */ + memset(free_i->free_segmap, 0xff, bitmap_size); + memset(free_i->free_secmap, 0xff, sec_bitmap_size); + + /* init free segmap information */ + free_i->start_segno = GET_SEGNO_FROM_SEG0(sbi, MAIN_BLKADDR(sbi)); + free_i->free_segments = 0; + free_i->free_sections = 0; + spin_lock_init(&free_i->segmap_lock); + return 0; +} + +static int build_curseg(struct f2fs_sb_info *sbi) +{ + struct curseg_info *array; + int i; + + array = kcalloc(NR_CURSEG_TYPE, sizeof(*array), GFP_KERNEL); + if (!array) + return -ENOMEM; + + SM_I(sbi)->curseg_array = array; + + for (i = 0; i < NR_CURSEG_TYPE; i++) { + mutex_init(&array[i].curseg_mutex); + array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL); + if (!array[i].sum_blk) + return -ENOMEM; + array[i].segno = NULL_SEGNO; + array[i].next_blkoff = 0; + } + return restore_curseg_summaries(sbi); +} + +static void build_sit_entries(struct f2fs_sb_info *sbi) +{ + struct sit_info *sit_i = SIT_I(sbi); + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + int sit_blk_cnt = SIT_BLK_CNT(sbi); + unsigned int i, start, end; + unsigned int readed, start_blk = 0; + int nrpages = MAX_BIO_BLOCKS(sbi); + + do { + readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT); + + start = start_blk * sit_i->sents_per_block; + end = (start_blk + readed) * sit_i->sents_per_block; + + for (; start < end && start < MAIN_SEGS(sbi); start++) { + struct seg_entry *se = &sit_i->sentries[start]; + struct f2fs_sit_block *sit_blk; + struct f2fs_sit_entry sit; + struct page *page; + + mutex_lock(&curseg->curseg_mutex); + for (i = 0; i < sits_in_cursum(sum); i++) { + if (le32_to_cpu(segno_in_journal(sum, i)) + == start) { + sit = sit_in_journal(sum, i); + mutex_unlock(&curseg->curseg_mutex); + goto got_it; + } + } + mutex_unlock(&curseg->curseg_mutex); + + page = get_current_sit_page(sbi, start); + sit_blk = (struct f2fs_sit_block *)page_address(page); + sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; + f2fs_put_page(page, 1); +got_it: + check_block_count(sbi, start, &sit); + seg_info_from_raw_sit(se, &sit); + if (sbi->segs_per_sec > 1) { + struct sec_entry *e = get_sec_entry(sbi, start); + e->valid_blocks += se->valid_blocks; + } + } + start_blk += readed; + } while (start_blk < sit_blk_cnt); +} + +static void init_free_segmap(struct f2fs_sb_info *sbi) +{ + unsigned int start; + int type; + + for (start = 0; start < MAIN_SEGS(sbi); start++) { + struct seg_entry *sentry = get_seg_entry(sbi, start); + if (!sentry->valid_blocks) + __set_free(sbi, start); + } + + /* set use the current segments */ + for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) { + struct curseg_info *curseg_t = CURSEG_I(sbi, type); + __set_test_and_inuse(sbi, curseg_t->segno); + } +} + +static void init_dirty_segmap(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int segno = 0, offset = 0; + unsigned short valid_blocks; + + while (1) { + /* find dirty segment based on free segmap */ + segno = find_next_inuse(free_i, MAIN_SEGS(sbi), offset); + if (segno >= MAIN_SEGS(sbi)) + break; + offset = segno + 1; + valid_blocks = get_valid_blocks(sbi, segno, 0); + if (valid_blocks == sbi->blocks_per_seg || !valid_blocks) + continue; + if (valid_blocks > sbi->blocks_per_seg) { + f2fs_bug_on(sbi, 1); + continue; + } + mutex_lock(&dirty_i->seglist_lock); + __locate_dirty_segment(sbi, segno, DIRTY); + mutex_unlock(&dirty_i->seglist_lock); + } +} + +static int init_victim_secmap(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); + + dirty_i->victim_secmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!dirty_i->victim_secmap) + return -ENOMEM; + return 0; +} + +static int build_dirty_segmap(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i; + unsigned int bitmap_size, i; + + /* allocate memory for dirty segments list information */ + dirty_i = kzalloc(sizeof(struct dirty_seglist_info), GFP_KERNEL); + if (!dirty_i) + return -ENOMEM; + + SM_I(sbi)->dirty_info = dirty_i; + mutex_init(&dirty_i->seglist_lock); + + bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); + + for (i = 0; i < NR_DIRTY_TYPE; i++) { + dirty_i->dirty_segmap[i] = kzalloc(bitmap_size, GFP_KERNEL); + if (!dirty_i->dirty_segmap[i]) + return -ENOMEM; + } + + init_dirty_segmap(sbi); + return init_victim_secmap(sbi); +} + +/* + * Update min, max modified time for cost-benefit GC algorithm + */ +static void init_min_max_mtime(struct f2fs_sb_info *sbi) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned int segno; + + mutex_lock(&sit_i->sentry_lock); + + sit_i->min_mtime = LLONG_MAX; + + for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { + unsigned int i; + unsigned long long mtime = 0; + + for (i = 0; i < sbi->segs_per_sec; i++) + mtime += get_seg_entry(sbi, segno + i)->mtime; + + mtime = div_u64(mtime, sbi->segs_per_sec); + + if (sit_i->min_mtime > mtime) + sit_i->min_mtime = mtime; + } + sit_i->max_mtime = get_mtime(sbi); + mutex_unlock(&sit_i->sentry_lock); +} + +int build_segment_manager(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + struct f2fs_sm_info *sm_info; + int err; + + sm_info = kzalloc(sizeof(struct f2fs_sm_info), GFP_KERNEL); + if (!sm_info) + return -ENOMEM; + + /* init sm info */ + sbi->sm_info = sm_info; + sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); + sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); + sm_info->segment_count = le32_to_cpu(raw_super->segment_count); + sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); + sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); + sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); + sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); + sm_info->rec_prefree_segments = sm_info->main_segments * + DEF_RECLAIM_PREFREE_SEGMENTS / 100; + sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; + sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; + sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; + + INIT_LIST_HEAD(&sm_info->discard_list); + sm_info->nr_discards = 0; + sm_info->max_discards = 0; + + sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS; + + INIT_LIST_HEAD(&sm_info->sit_entry_set); + + if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) { + err = create_flush_cmd_control(sbi); + if (err) + return err; + } + + err = build_sit_info(sbi); + if (err) + return err; + err = build_free_segmap(sbi); + if (err) + return err; + err = build_curseg(sbi); + if (err) + return err; + + /* reinit free segmap based on SIT */ + build_sit_entries(sbi); + + init_free_segmap(sbi); + err = build_dirty_segmap(sbi); + if (err) + return err; + + init_min_max_mtime(sbi); + return 0; +} + +static void discard_dirty_segmap(struct f2fs_sb_info *sbi, + enum dirty_type dirty_type) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + + mutex_lock(&dirty_i->seglist_lock); + kfree(dirty_i->dirty_segmap[dirty_type]); + dirty_i->nr_dirty[dirty_type] = 0; + mutex_unlock(&dirty_i->seglist_lock); +} + +static void destroy_victim_secmap(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + kfree(dirty_i->victim_secmap); +} + +static void destroy_dirty_segmap(struct f2fs_sb_info *sbi) +{ + struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); + int i; + + if (!dirty_i) + return; + + /* discard pre-free/dirty segments list */ + for (i = 0; i < NR_DIRTY_TYPE; i++) + discard_dirty_segmap(sbi, i); + + destroy_victim_secmap(sbi); + SM_I(sbi)->dirty_info = NULL; + kfree(dirty_i); +} + +static void destroy_curseg(struct f2fs_sb_info *sbi) +{ + struct curseg_info *array = SM_I(sbi)->curseg_array; + int i; + + if (!array) + return; + SM_I(sbi)->curseg_array = NULL; + for (i = 0; i < NR_CURSEG_TYPE; i++) + kfree(array[i].sum_blk); + kfree(array); +} + +static void destroy_free_segmap(struct f2fs_sb_info *sbi) +{ + struct free_segmap_info *free_i = SM_I(sbi)->free_info; + if (!free_i) + return; + SM_I(sbi)->free_info = NULL; + kfree(free_i->free_segmap); + kfree(free_i->free_secmap); + kfree(free_i); +} + +static void destroy_sit_info(struct f2fs_sb_info *sbi) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned int start; + + if (!sit_i) + return; + + if (sit_i->sentries) { + for (start = 0; start < MAIN_SEGS(sbi); start++) { + kfree(sit_i->sentries[start].cur_valid_map); + kfree(sit_i->sentries[start].ckpt_valid_map); + } + } + kfree(sit_i->tmp_map); + + vfree(sit_i->sentries); + vfree(sit_i->sec_entries); + kfree(sit_i->dirty_sentries_bitmap); + + SM_I(sbi)->sit_info = NULL; + kfree(sit_i->sit_bitmap); + kfree(sit_i); +} + +void destroy_segment_manager(struct f2fs_sb_info *sbi) +{ + struct f2fs_sm_info *sm_info = SM_I(sbi); + + if (!sm_info) + return; + destroy_flush_cmd_control(sbi); + destroy_dirty_segmap(sbi); + destroy_curseg(sbi); + destroy_free_segmap(sbi); + destroy_sit_info(sbi); + sbi->sm_info = NULL; + kfree(sm_info); +} + +int __init create_segment_manager_caches(void) +{ + discard_entry_slab = f2fs_kmem_cache_create("discard_entry", + sizeof(struct discard_entry)); + if (!discard_entry_slab) + goto fail; + + sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", + sizeof(struct sit_entry_set)); + if (!sit_entry_set_slab) + goto destory_discard_entry; + + inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry", + sizeof(struct inmem_pages)); + if (!inmem_entry_slab) + goto destroy_sit_entry_set; + return 0; + +destroy_sit_entry_set: + kmem_cache_destroy(sit_entry_set_slab); +destory_discard_entry: + kmem_cache_destroy(discard_entry_slab); +fail: + return -ENOMEM; +} + +void destroy_segment_manager_caches(void) +{ + kmem_cache_destroy(sit_entry_set_slab); + kmem_cache_destroy(discard_entry_slab); + kmem_cache_destroy(inmem_entry_slab); +} diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h new file mode 100644 index 000000000000..85d7fa7514b2 --- /dev/null +++ b/fs/f2fs/segment.h @@ -0,0 +1,751 @@ +/* + * fs/f2fs/segment.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +/* constant macro */ +#define NULL_SEGNO ((unsigned int)(~0)) +#define NULL_SECNO ((unsigned int)(~0)) + +#define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */ + +/* L: Logical segment # in volume, R: Relative segment # in main area */ +#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) +#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno) + +#define IS_DATASEG(t) (t <= CURSEG_COLD_DATA) +#define IS_NODESEG(t) (t >= CURSEG_HOT_NODE) + +#define IS_CURSEG(sbi, seg) \ + ((seg == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ + (seg == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ + (seg == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \ + (seg == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \ + (seg == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \ + (seg == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno)) + +#define IS_CURSEC(sbi, secno) \ + ((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \ + sbi->segs_per_sec) || \ + (secno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \ + sbi->segs_per_sec) || \ + (secno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \ + sbi->segs_per_sec) || \ + (secno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \ + sbi->segs_per_sec) || \ + (secno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \ + sbi->segs_per_sec) || \ + (secno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \ + sbi->segs_per_sec)) \ + +#define MAIN_BLKADDR(sbi) (SM_I(sbi)->main_blkaddr) +#define SEG0_BLKADDR(sbi) (SM_I(sbi)->seg0_blkaddr) + +#define MAIN_SEGS(sbi) (SM_I(sbi)->main_segments) +#define MAIN_SECS(sbi) (sbi->total_sections) + +#define TOTAL_SEGS(sbi) (SM_I(sbi)->segment_count) +#define TOTAL_BLKS(sbi) (TOTAL_SEGS(sbi) << sbi->log_blocks_per_seg) + +#define MAX_BLKADDR(sbi) (SEG0_BLKADDR(sbi) + TOTAL_BLKS(sbi)) +#define SEGMENT_SIZE(sbi) (1ULL << (sbi->log_blocksize + \ + sbi->log_blocks_per_seg)) + +#define START_BLOCK(sbi, segno) (SEG0_BLKADDR(sbi) + \ + (GET_R2L_SEGNO(FREE_I(sbi), segno) << sbi->log_blocks_per_seg)) + +#define NEXT_FREE_BLKADDR(sbi, curseg) \ + (START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff) + +#define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) ((blk_addr) - SEG0_BLKADDR(sbi)) +#define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \ + (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg) +#define GET_BLKOFF_FROM_SEG0(sbi, blk_addr) \ + (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1)) + +#define GET_SEGNO(sbi, blk_addr) \ + (((blk_addr == NULL_ADDR) || (blk_addr == NEW_ADDR)) ? \ + NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ + GET_SEGNO_FROM_SEG0(sbi, blk_addr))) +#define GET_SECNO(sbi, segno) \ + ((segno) / sbi->segs_per_sec) +#define GET_ZONENO_FROM_SEGNO(sbi, segno) \ + ((segno / sbi->segs_per_sec) / sbi->secs_per_zone) + +#define GET_SUM_BLOCK(sbi, segno) \ + ((sbi->sm_info->ssa_blkaddr) + segno) + +#define GET_SUM_TYPE(footer) ((footer)->entry_type) +#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = type) + +#define SIT_ENTRY_OFFSET(sit_i, segno) \ + (segno % sit_i->sents_per_block) +#define SIT_BLOCK_OFFSET(segno) \ + (segno / SIT_ENTRY_PER_BLOCK) +#define START_SEGNO(segno) \ + (SIT_BLOCK_OFFSET(segno) * SIT_ENTRY_PER_BLOCK) +#define SIT_BLK_CNT(sbi) \ + ((MAIN_SEGS(sbi) + SIT_ENTRY_PER_BLOCK - 1) / SIT_ENTRY_PER_BLOCK) +#define f2fs_bitmap_size(nr) \ + (BITS_TO_LONGS(nr) * sizeof(unsigned long)) + +#define SECTOR_FROM_BLOCK(blk_addr) \ + (((sector_t)blk_addr) << F2FS_LOG_SECTORS_PER_BLOCK) +#define SECTOR_TO_BLOCK(sectors) \ + (sectors >> F2FS_LOG_SECTORS_PER_BLOCK) +#define MAX_BIO_BLOCKS(sbi) \ + ((int)min((int)max_hw_blocks(sbi), BIO_MAX_PAGES)) + +/* + * indicate a block allocation direction: RIGHT and LEFT. + * RIGHT means allocating new sections towards the end of volume. + * LEFT means the opposite direction. + */ +enum { + ALLOC_RIGHT = 0, + ALLOC_LEFT +}; + +/* + * In the victim_sel_policy->alloc_mode, there are two block allocation modes. + * LFS writes data sequentially with cleaning operations. + * SSR (Slack Space Recycle) reuses obsolete space without cleaning operations. + */ +enum { + LFS = 0, + SSR +}; + +/* + * In the victim_sel_policy->gc_mode, there are two gc, aka cleaning, modes. + * GC_CB is based on cost-benefit algorithm. + * GC_GREEDY is based on greedy algorithm. + */ +enum { + GC_CB = 0, + GC_GREEDY +}; + +/* + * BG_GC means the background cleaning job. + * FG_GC means the on-demand cleaning job. + */ +enum { + BG_GC = 0, + FG_GC +}; + +/* for a function parameter to select a victim segment */ +struct victim_sel_policy { + int alloc_mode; /* LFS or SSR */ + int gc_mode; /* GC_CB or GC_GREEDY */ + unsigned long *dirty_segmap; /* dirty segment bitmap */ + unsigned int max_search; /* maximum # of segments to search */ + unsigned int offset; /* last scanned bitmap offset */ + unsigned int ofs_unit; /* bitmap search unit */ + unsigned int min_cost; /* minimum cost */ + unsigned int min_segno; /* segment # having min. cost */ +}; + +struct seg_entry { + unsigned short valid_blocks; /* # of valid blocks */ + unsigned char *cur_valid_map; /* validity bitmap of blocks */ + /* + * # of valid blocks and the validity bitmap stored in the the last + * checkpoint pack. This information is used by the SSR mode. + */ + unsigned short ckpt_valid_blocks; + unsigned char *ckpt_valid_map; + unsigned char type; /* segment type like CURSEG_XXX_TYPE */ + unsigned long long mtime; /* modification time of the segment */ +}; + +struct sec_entry { + unsigned int valid_blocks; /* # of valid blocks in a section */ +}; + +struct segment_allocation { + void (*allocate_segment)(struct f2fs_sb_info *, int, bool); +}; + +struct inmem_pages { + struct list_head list; + struct page *page; +}; + +struct sit_info { + const struct segment_allocation *s_ops; + + block_t sit_base_addr; /* start block address of SIT area */ + block_t sit_blocks; /* # of blocks used by SIT area */ + block_t written_valid_blocks; /* # of valid blocks in main area */ + char *sit_bitmap; /* SIT bitmap pointer */ + unsigned int bitmap_size; /* SIT bitmap size */ + + unsigned long *tmp_map; /* bitmap for temporal use */ + unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ + unsigned int dirty_sentries; /* # of dirty sentries */ + unsigned int sents_per_block; /* # of SIT entries per block */ + struct mutex sentry_lock; /* to protect SIT cache */ + struct seg_entry *sentries; /* SIT segment-level cache */ + struct sec_entry *sec_entries; /* SIT section-level cache */ + + /* for cost-benefit algorithm in cleaning procedure */ + unsigned long long elapsed_time; /* elapsed time after mount */ + unsigned long long mounted_time; /* mount time */ + unsigned long long min_mtime; /* min. modification time */ + unsigned long long max_mtime; /* max. modification time */ +}; + +struct free_segmap_info { + unsigned int start_segno; /* start segment number logically */ + unsigned int free_segments; /* # of free segments */ + unsigned int free_sections; /* # of free sections */ + spinlock_t segmap_lock; /* free segmap lock */ + unsigned long *free_segmap; /* free segment bitmap */ + unsigned long *free_secmap; /* free section bitmap */ +}; + +/* Notice: The order of dirty type is same with CURSEG_XXX in f2fs.h */ +enum dirty_type { + DIRTY_HOT_DATA, /* dirty segments assigned as hot data logs */ + DIRTY_WARM_DATA, /* dirty segments assigned as warm data logs */ + DIRTY_COLD_DATA, /* dirty segments assigned as cold data logs */ + DIRTY_HOT_NODE, /* dirty segments assigned as hot node logs */ + DIRTY_WARM_NODE, /* dirty segments assigned as warm node logs */ + DIRTY_COLD_NODE, /* dirty segments assigned as cold node logs */ + DIRTY, /* to count # of dirty segments */ + PRE, /* to count # of entirely obsolete segments */ + NR_DIRTY_TYPE +}; + +struct dirty_seglist_info { + const struct victim_selection *v_ops; /* victim selction operation */ + unsigned long *dirty_segmap[NR_DIRTY_TYPE]; + struct mutex seglist_lock; /* lock for segment bitmaps */ + int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */ + unsigned long *victim_secmap; /* background GC victims */ +}; + +/* victim selection function for cleaning and SSR */ +struct victim_selection { + int (*get_victim)(struct f2fs_sb_info *, unsigned int *, + int, int, char); +}; + +/* for active log information */ +struct curseg_info { + struct mutex curseg_mutex; /* lock for consistency */ + struct f2fs_summary_block *sum_blk; /* cached summary block */ + unsigned char alloc_type; /* current allocation type */ + unsigned int segno; /* current segment number */ + unsigned short next_blkoff; /* next block offset to write */ + unsigned int zone; /* current zone number */ + unsigned int next_segno; /* preallocated segment */ +}; + +struct sit_entry_set { + struct list_head set_list; /* link with all sit sets */ + unsigned int start_segno; /* start segno of sits in set */ + unsigned int entry_cnt; /* the # of sit entries in set */ +}; + +/* + * inline functions + */ +static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type) +{ + return (struct curseg_info *)(SM_I(sbi)->curseg_array + type); +} + +static inline struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct sit_info *sit_i = SIT_I(sbi); + return &sit_i->sentries[segno]; +} + +static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct sit_info *sit_i = SIT_I(sbi); + return &sit_i->sec_entries[GET_SECNO(sbi, segno)]; +} + +static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi, + unsigned int segno, int section) +{ + /* + * In order to get # of valid blocks in a section instantly from many + * segments, f2fs manages two counting structures separately. + */ + if (section > 1) + return get_sec_entry(sbi, segno)->valid_blocks; + else + return get_seg_entry(sbi, segno)->valid_blocks; +} + +static inline void seg_info_from_raw_sit(struct seg_entry *se, + struct f2fs_sit_entry *rs) +{ + se->valid_blocks = GET_SIT_VBLOCKS(rs); + se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); + memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); + memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); + se->type = GET_SIT_TYPE(rs); + se->mtime = le64_to_cpu(rs->mtime); +} + +static inline void seg_info_to_raw_sit(struct seg_entry *se, + struct f2fs_sit_entry *rs) +{ + unsigned short raw_vblocks = (se->type << SIT_VBLOCKS_SHIFT) | + se->valid_blocks; + rs->vblocks = cpu_to_le16(raw_vblocks); + memcpy(rs->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); + se->ckpt_valid_blocks = se->valid_blocks; + rs->mtime = cpu_to_le64(se->mtime); +} + +static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, + unsigned int max, unsigned int segno) +{ + unsigned int ret; + spin_lock(&free_i->segmap_lock); + ret = find_next_bit(free_i->free_segmap, max, segno); + spin_unlock(&free_i->segmap_lock); + return ret; +} + +static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno) +{ + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int secno = segno / sbi->segs_per_sec; + unsigned int start_segno = secno * sbi->segs_per_sec; + unsigned int next; + + spin_lock(&free_i->segmap_lock); + clear_bit(segno, free_i->free_segmap); + free_i->free_segments++; + + next = find_next_bit(free_i->free_segmap, + start_segno + sbi->segs_per_sec, start_segno); + if (next >= start_segno + sbi->segs_per_sec) { + clear_bit(secno, free_i->free_secmap); + free_i->free_sections++; + } + spin_unlock(&free_i->segmap_lock); +} + +static inline void __set_inuse(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int secno = segno / sbi->segs_per_sec; + set_bit(segno, free_i->free_segmap); + free_i->free_segments--; + if (!test_and_set_bit(secno, free_i->free_secmap)) + free_i->free_sections--; +} + +static inline void __set_test_and_free(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int secno = segno / sbi->segs_per_sec; + unsigned int start_segno = secno * sbi->segs_per_sec; + unsigned int next; + + spin_lock(&free_i->segmap_lock); + if (test_and_clear_bit(segno, free_i->free_segmap)) { + free_i->free_segments++; + + next = find_next_bit(free_i->free_segmap, + start_segno + sbi->segs_per_sec, start_segno); + if (next >= start_segno + sbi->segs_per_sec) { + if (test_and_clear_bit(secno, free_i->free_secmap)) + free_i->free_sections++; + } + } + spin_unlock(&free_i->segmap_lock); +} + +static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi, + unsigned int segno) +{ + struct free_segmap_info *free_i = FREE_I(sbi); + unsigned int secno = segno / sbi->segs_per_sec; + spin_lock(&free_i->segmap_lock); + if (!test_and_set_bit(segno, free_i->free_segmap)) { + free_i->free_segments--; + if (!test_and_set_bit(secno, free_i->free_secmap)) + free_i->free_sections--; + } + spin_unlock(&free_i->segmap_lock); +} + +static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, + void *dst_addr) +{ + struct sit_info *sit_i = SIT_I(sbi); + memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); +} + +static inline block_t written_block_count(struct f2fs_sb_info *sbi) +{ + return SIT_I(sbi)->written_valid_blocks; +} + +static inline unsigned int free_segments(struct f2fs_sb_info *sbi) +{ + return FREE_I(sbi)->free_segments; +} + +static inline int reserved_segments(struct f2fs_sb_info *sbi) +{ + return SM_I(sbi)->reserved_segments; +} + +static inline unsigned int free_sections(struct f2fs_sb_info *sbi) +{ + return FREE_I(sbi)->free_sections; +} + +static inline unsigned int prefree_segments(struct f2fs_sb_info *sbi) +{ + return DIRTY_I(sbi)->nr_dirty[PRE]; +} + +static inline unsigned int dirty_segments(struct f2fs_sb_info *sbi) +{ + return DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_DATA] + + DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_DATA] + + DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_DATA] + + DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_NODE] + + DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_NODE] + + DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_NODE]; +} + +static inline int overprovision_segments(struct f2fs_sb_info *sbi) +{ + return SM_I(sbi)->ovp_segments; +} + +static inline int overprovision_sections(struct f2fs_sb_info *sbi) +{ + return ((unsigned int) overprovision_segments(sbi)) / sbi->segs_per_sec; +} + +static inline int reserved_sections(struct f2fs_sb_info *sbi) +{ + return ((unsigned int) reserved_segments(sbi)) / sbi->segs_per_sec; +} + +static inline bool need_SSR(struct f2fs_sb_info *sbi) +{ + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + return free_sections(sbi) <= (node_secs + 2 * dent_secs + + reserved_sections(sbi) + 1); +} + +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) +{ + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + + if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) + return false; + + return (free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + + reserved_sections(sbi)); +} + +static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi) +{ + return prefree_segments(sbi) > SM_I(sbi)->rec_prefree_segments; +} + +static inline int utilization(struct f2fs_sb_info *sbi) +{ + return div_u64((u64)valid_user_blocks(sbi) * 100, + sbi->user_block_count); +} + +/* + * Sometimes f2fs may be better to drop out-of-place update policy. + * And, users can control the policy through sysfs entries. + * There are five policies with triggering conditions as follows. + * F2FS_IPU_FORCE - all the time, + * F2FS_IPU_SSR - if SSR mode is activated, + * F2FS_IPU_UTIL - if FS utilization is over threashold, + * F2FS_IPU_SSR_UTIL - if SSR mode is activated and FS utilization is over + * threashold, + * F2FS_IPU_FSYNC - activated in fsync path only for high performance flash + * storages. IPU will be triggered only if the # of dirty + * pages over min_fsync_blocks. + * F2FS_IPUT_DISABLE - disable IPU. (=default option) + */ +#define DEF_MIN_IPU_UTIL 70 +#define DEF_MIN_FSYNC_BLOCKS 8 + +enum { + F2FS_IPU_FORCE, + F2FS_IPU_SSR, + F2FS_IPU_UTIL, + F2FS_IPU_SSR_UTIL, + F2FS_IPU_FSYNC, +}; + +static inline bool need_inplace_update(struct inode *inode) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + unsigned int policy = SM_I(sbi)->ipu_policy; + + /* IPU can be done only for the user data */ + if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) + return false; + + if (policy & (0x1 << F2FS_IPU_FORCE)) + return true; + if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) + return true; + if (policy & (0x1 << F2FS_IPU_UTIL) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + if (policy & (0x1 << F2FS_IPU_SSR_UTIL) && need_SSR(sbi) && + utilization(sbi) > SM_I(sbi)->min_ipu_util) + return true; + + /* this is only set during fdatasync */ + if (policy & (0x1 << F2FS_IPU_FSYNC) && + is_inode_flag_set(F2FS_I(inode), FI_NEED_IPU)) + return true; + + return false; +} + +static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi, + int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + return curseg->segno; +} + +static inline unsigned char curseg_alloc_type(struct f2fs_sb_info *sbi, + int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + return curseg->alloc_type; +} + +static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + return curseg->next_blkoff; +} + +#ifdef CONFIG_F2FS_CHECK_FS +static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) +{ + BUG_ON(segno > TOTAL_SEGS(sbi) - 1); +} + +static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) +{ + BUG_ON(blk_addr < SEG0_BLKADDR(sbi)); + BUG_ON(blk_addr >= MAX_BLKADDR(sbi)); +} + +/* + * Summary block is always treated as an invalid block + */ +static inline void check_block_count(struct f2fs_sb_info *sbi, + int segno, struct f2fs_sit_entry *raw_sit) +{ + bool is_valid = test_bit_le(0, raw_sit->valid_map) ? true : false; + int valid_blocks = 0; + int cur_pos = 0, next_pos; + + /* check segment usage */ + BUG_ON(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg); + + /* check boundary of a given segment number */ + BUG_ON(segno > TOTAL_SEGS(sbi) - 1); + + /* check bitmap with valid block count */ + do { + if (is_valid) { + next_pos = find_next_zero_bit_le(&raw_sit->valid_map, + sbi->blocks_per_seg, + cur_pos); + valid_blocks += next_pos - cur_pos; + } else + next_pos = find_next_bit_le(&raw_sit->valid_map, + sbi->blocks_per_seg, + cur_pos); + cur_pos = next_pos; + is_valid = !is_valid; + } while (cur_pos < sbi->blocks_per_seg); + BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks); +} +#else +static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) +{ + if (segno > TOTAL_SEGS(sbi) - 1) + set_sbi_flag(sbi, SBI_NEED_FSCK); +} + +static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) +{ + if (blk_addr < SEG0_BLKADDR(sbi) || blk_addr >= MAX_BLKADDR(sbi)) + set_sbi_flag(sbi, SBI_NEED_FSCK); +} + +/* + * Summary block is always treated as an invalid block + */ +static inline void check_block_count(struct f2fs_sb_info *sbi, + int segno, struct f2fs_sit_entry *raw_sit) +{ + /* check segment usage */ + if (GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg) + set_sbi_flag(sbi, SBI_NEED_FSCK); + + /* check boundary of a given segment number */ + if (segno > TOTAL_SEGS(sbi) - 1) + set_sbi_flag(sbi, SBI_NEED_FSCK); +} +#endif + +static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, + unsigned int start) +{ + struct sit_info *sit_i = SIT_I(sbi); + unsigned int offset = SIT_BLOCK_OFFSET(start); + block_t blk_addr = sit_i->sit_base_addr + offset; + + check_seg_range(sbi, start); + + /* calculate sit block address */ + if (f2fs_test_bit(offset, sit_i->sit_bitmap)) + blk_addr += sit_i->sit_blocks; + + return blk_addr; +} + +static inline pgoff_t next_sit_addr(struct f2fs_sb_info *sbi, + pgoff_t block_addr) +{ + struct sit_info *sit_i = SIT_I(sbi); + block_addr -= sit_i->sit_base_addr; + if (block_addr < sit_i->sit_blocks) + block_addr += sit_i->sit_blocks; + else + block_addr -= sit_i->sit_blocks; + + return block_addr + sit_i->sit_base_addr; +} + +static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) +{ + unsigned int block_off = SIT_BLOCK_OFFSET(start); + + f2fs_change_bit(block_off, sit_i->sit_bitmap); +} + +static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi) +{ + struct sit_info *sit_i = SIT_I(sbi); + return sit_i->elapsed_time + CURRENT_TIME_SEC.tv_sec - + sit_i->mounted_time; +} + +static inline void set_summary(struct f2fs_summary *sum, nid_t nid, + unsigned int ofs_in_node, unsigned char version) +{ + sum->nid = cpu_to_le32(nid); + sum->ofs_in_node = cpu_to_le16(ofs_in_node); + sum->version = version; +} + +static inline block_t start_sum_block(struct f2fs_sb_info *sbi) +{ + return __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); +} + +static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) +{ + return __start_cp_addr(sbi) + + le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count) + - (base + 1) + type; +} + +static inline bool sec_usage_check(struct f2fs_sb_info *sbi, unsigned int secno) +{ + if (IS_CURSEC(sbi, secno) || (sbi->cur_victim_sec == secno)) + return true; + return false; +} + +static inline unsigned int max_hw_blocks(struct f2fs_sb_info *sbi) +{ + struct block_device *bdev = sbi->sb->s_bdev; + struct request_queue *q = bdev_get_queue(bdev); + return SECTOR_TO_BLOCK(queue_max_sectors(q)); +} + +/* + * It is very important to gather dirty pages and write at once, so that we can + * submit a big bio without interfering other data writes. + * By default, 512 pages for directory data, + * 512 pages (2MB) * 3 for three types of nodes, and + * max_bio_blocks for meta are set. + */ +static inline int nr_pages_to_skip(struct f2fs_sb_info *sbi, int type) +{ + if (sbi->sb->s_bdi->dirty_exceeded) + return 0; + + if (type == DATA) + return sbi->blocks_per_seg; + else if (type == NODE) + return 3 * sbi->blocks_per_seg; + else if (type == META) + return MAX_BIO_BLOCKS(sbi); + else + return 0; +} + +/* + * When writing pages, it'd better align nr_to_write for segment size. + */ +static inline long nr_pages_to_write(struct f2fs_sb_info *sbi, int type, + struct writeback_control *wbc) +{ + long nr_to_write, desired; + + if (wbc->sync_mode != WB_SYNC_NONE) + return 0; + + nr_to_write = wbc->nr_to_write; + + if (type == DATA) + desired = 4096; + else if (type == NODE) + desired = 3 * max_hw_blocks(sbi); + else + desired = MAX_BIO_BLOCKS(sbi); + + wbc->nr_to_write = desired; + return desired - nr_to_write; +} diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c new file mode 100644 index 000000000000..413791a6d51a --- /dev/null +++ b/fs/f2fs/super.c @@ -0,0 +1,1348 @@ +/* + * fs/f2fs/super.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "f2fs.h" +#include "node.h" +#include "segment.h" +#include "xattr.h" +#include "gc.h" +#include "trace.h" + +#define CREATE_TRACE_POINTS +#include + +static struct proc_dir_entry *f2fs_proc_root; +static struct kmem_cache *f2fs_inode_cachep; +static struct kset *f2fs_kset; + +enum { + Opt_gc_background, + Opt_disable_roll_forward, + Opt_norecovery, + Opt_discard, + Opt_noheap, + Opt_user_xattr, + Opt_nouser_xattr, + Opt_acl, + Opt_noacl, + Opt_active_logs, + Opt_disable_ext_identify, + Opt_inline_xattr, + Opt_inline_data, + Opt_inline_dentry, + Opt_flush_merge, + Opt_nobarrier, + Opt_fastboot, + Opt_extent_cache, + Opt_noinline_data, + Opt_err, +}; + +static match_table_t f2fs_tokens = { + {Opt_gc_background, "background_gc=%s"}, + {Opt_disable_roll_forward, "disable_roll_forward"}, + {Opt_norecovery, "norecovery"}, + {Opt_discard, "discard"}, + {Opt_noheap, "no_heap"}, + {Opt_user_xattr, "user_xattr"}, + {Opt_nouser_xattr, "nouser_xattr"}, + {Opt_acl, "acl"}, + {Opt_noacl, "noacl"}, + {Opt_active_logs, "active_logs=%u"}, + {Opt_disable_ext_identify, "disable_ext_identify"}, + {Opt_inline_xattr, "inline_xattr"}, + {Opt_inline_data, "inline_data"}, + {Opt_inline_dentry, "inline_dentry"}, + {Opt_flush_merge, "flush_merge"}, + {Opt_nobarrier, "nobarrier"}, + {Opt_fastboot, "fastboot"}, + {Opt_extent_cache, "extent_cache"}, + {Opt_noinline_data, "noinline_data"}, + {Opt_err, NULL}, +}; + +/* Sysfs support for f2fs */ +enum { + GC_THREAD, /* struct f2fs_gc_thread */ + SM_INFO, /* struct f2fs_sm_info */ + NM_INFO, /* struct f2fs_nm_info */ + F2FS_SBI, /* struct f2fs_sb_info */ +}; + +struct f2fs_attr { + struct attribute attr; + ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *); + ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *, + const char *, size_t); + int struct_type; + int offset; +}; + +static unsigned char *__struct_ptr(struct f2fs_sb_info *sbi, int struct_type) +{ + if (struct_type == GC_THREAD) + return (unsigned char *)sbi->gc_thread; + else if (struct_type == SM_INFO) + return (unsigned char *)SM_I(sbi); + else if (struct_type == NM_INFO) + return (unsigned char *)NM_I(sbi); + else if (struct_type == F2FS_SBI) + return (unsigned char *)sbi; + return NULL; +} + +static ssize_t f2fs_sbi_show(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, char *buf) +{ + unsigned char *ptr = NULL; + unsigned int *ui; + + ptr = __struct_ptr(sbi, a->struct_type); + if (!ptr) + return -EINVAL; + + ui = (unsigned int *)(ptr + a->offset); + + return snprintf(buf, PAGE_SIZE, "%u\n", *ui); +} + +static ssize_t f2fs_sbi_store(struct f2fs_attr *a, + struct f2fs_sb_info *sbi, + const char *buf, size_t count) +{ + unsigned char *ptr; + unsigned long t; + unsigned int *ui; + ssize_t ret; + + ptr = __struct_ptr(sbi, a->struct_type); + if (!ptr) + return -EINVAL; + + ui = (unsigned int *)(ptr + a->offset); + + ret = kstrtoul(skip_spaces(buf), 0, &t); + if (ret < 0) + return ret; + *ui = t; + return count; +} + +static ssize_t f2fs_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); + + return a->show ? a->show(a, sbi, buf) : 0; +} + +static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); + + return a->store ? a->store(a, sbi, buf, len) : 0; +} + +static void f2fs_sb_release(struct kobject *kobj) +{ + struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, + s_kobj); + complete(&sbi->s_kobj_unregister); +} + +#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ +static struct f2fs_attr f2fs_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ + .struct_type = _struct_type, \ + .offset = _offset \ +} + +#define F2FS_RW_ATTR(struct_type, struct_name, name, elname) \ + F2FS_ATTR_OFFSET(struct_type, name, 0644, \ + f2fs_sbi_show, f2fs_sbi_store, \ + offsetof(struct struct_name, elname)) + +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time); +F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_idle, gc_idle); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, max_small_discards, max_discards); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, batched_trim_sections, trim_sections); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util); +F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks); +F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search); +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level); + +#define ATTR_LIST(name) (&f2fs_attr_##name.attr) +static struct attribute *f2fs_attrs[] = { + ATTR_LIST(gc_min_sleep_time), + ATTR_LIST(gc_max_sleep_time), + ATTR_LIST(gc_no_gc_sleep_time), + ATTR_LIST(gc_idle), + ATTR_LIST(reclaim_segments), + ATTR_LIST(max_small_discards), + ATTR_LIST(batched_trim_sections), + ATTR_LIST(ipu_policy), + ATTR_LIST(min_ipu_util), + ATTR_LIST(min_fsync_blocks), + ATTR_LIST(max_victim_search), + ATTR_LIST(dir_level), + ATTR_LIST(ram_thresh), + NULL, +}; + +static const struct sysfs_ops f2fs_attr_ops = { + .show = f2fs_attr_show, + .store = f2fs_attr_store, +}; + +static struct kobj_type f2fs_ktype = { + .default_attrs = f2fs_attrs, + .sysfs_ops = &f2fs_attr_ops, + .release = f2fs_sb_release, +}; + +void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk("%sF2FS-fs (%s): %pV\n", level, sb->s_id, &vaf); + va_end(args); +} + +static void init_once(void *foo) +{ + struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo; + + inode_init_once(&fi->vfs_inode); +} + +static int parse_options(struct super_block *sb, char *options) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + substring_t args[MAX_OPT_ARGS]; + char *p, *name; + int arg = 0; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + /* + * Initialize args struct so we know whether arg was + * found; some options take optional arguments. + */ + args[0].to = args[0].from = NULL; + token = match_token(p, f2fs_tokens, args); + + switch (token) { + case Opt_gc_background: + name = match_strdup(&args[0]); + + if (!name) + return -ENOMEM; + if (strlen(name) == 2 && !strncmp(name, "on", 2)) + set_opt(sbi, BG_GC); + else if (strlen(name) == 3 && !strncmp(name, "off", 3)) + clear_opt(sbi, BG_GC); + else { + kfree(name); + return -EINVAL; + } + kfree(name); + break; + case Opt_disable_roll_forward: + set_opt(sbi, DISABLE_ROLL_FORWARD); + break; + case Opt_norecovery: + /* this option mounts f2fs with ro */ + set_opt(sbi, DISABLE_ROLL_FORWARD); + if (!f2fs_readonly(sb)) + return -EINVAL; + break; + case Opt_discard: + set_opt(sbi, DISCARD); + break; + case Opt_noheap: + set_opt(sbi, NOHEAP); + break; +#ifdef CONFIG_F2FS_FS_XATTR + case Opt_user_xattr: + set_opt(sbi, XATTR_USER); + break; + case Opt_nouser_xattr: + clear_opt(sbi, XATTR_USER); + break; + case Opt_inline_xattr: + set_opt(sbi, INLINE_XATTR); + break; +#else + case Opt_user_xattr: + f2fs_msg(sb, KERN_INFO, + "user_xattr options not supported"); + break; + case Opt_nouser_xattr: + f2fs_msg(sb, KERN_INFO, + "nouser_xattr options not supported"); + break; + case Opt_inline_xattr: + f2fs_msg(sb, KERN_INFO, + "inline_xattr options not supported"); + break; +#endif +#ifdef CONFIG_F2FS_FS_POSIX_ACL + case Opt_acl: + set_opt(sbi, POSIX_ACL); + break; + case Opt_noacl: + clear_opt(sbi, POSIX_ACL); + break; +#else + case Opt_acl: + f2fs_msg(sb, KERN_INFO, "acl options not supported"); + break; + case Opt_noacl: + f2fs_msg(sb, KERN_INFO, "noacl options not supported"); + break; +#endif + case Opt_active_logs: + if (args->from && match_int(args, &arg)) + return -EINVAL; + if (arg != 2 && arg != 4 && arg != NR_CURSEG_TYPE) + return -EINVAL; + sbi->active_logs = arg; + break; + case Opt_disable_ext_identify: + set_opt(sbi, DISABLE_EXT_IDENTIFY); + break; + case Opt_inline_data: + set_opt(sbi, INLINE_DATA); + break; + case Opt_inline_dentry: + set_opt(sbi, INLINE_DENTRY); + break; + case Opt_flush_merge: + set_opt(sbi, FLUSH_MERGE); + break; + case Opt_nobarrier: + set_opt(sbi, NOBARRIER); + break; + case Opt_fastboot: + set_opt(sbi, FASTBOOT); + break; + case Opt_extent_cache: + set_opt(sbi, EXTENT_CACHE); + break; + case Opt_noinline_data: + clear_opt(sbi, INLINE_DATA); + break; + default: + f2fs_msg(sb, KERN_ERR, + "Unrecognized mount option \"%s\" or missing value", + p); + return -EINVAL; + } + } + return 0; +} + +static struct inode *f2fs_alloc_inode(struct super_block *sb) +{ + struct f2fs_inode_info *fi; + + fi = kmem_cache_alloc(f2fs_inode_cachep, GFP_F2FS_ZERO); + if (!fi) + return NULL; + + init_once((void *) fi); + + /* Initialize f2fs-specific inode info */ + fi->vfs_inode.i_version = 1; + atomic_set(&fi->dirty_pages, 0); + fi->i_current_depth = 1; + fi->i_advise = 0; + rwlock_init(&fi->ext_lock); + init_rwsem(&fi->i_sem); + INIT_RADIX_TREE(&fi->inmem_root, GFP_NOFS); + INIT_LIST_HEAD(&fi->inmem_pages); + mutex_init(&fi->inmem_lock); + + set_inode_flag(fi, FI_NEW_INODE); + + if (test_opt(F2FS_SB(sb), INLINE_XATTR)) + set_inode_flag(fi, FI_INLINE_XATTR); + + /* Will be used by directory only */ + fi->i_dir_level = F2FS_SB(sb)->dir_level; + + return &fi->vfs_inode; +} + +static int f2fs_drop_inode(struct inode *inode) +{ + /* + * This is to avoid a deadlock condition like below. + * writeback_single_inode(inode) + * - f2fs_write_data_page + * - f2fs_gc -> iput -> evict + * - inode_wait_for_writeback(inode) + */ + if (!inode_unhashed(inode) && inode->i_state & I_SYNC) + return 0; + return generic_drop_inode(inode); +} + +/* + * f2fs_dirty_inode() is called from __mark_inode_dirty() + * + * We should call set_dirty_inode to write the dirty inode through write_inode. + */ +static void f2fs_dirty_inode(struct inode *inode, int flags) +{ + set_inode_flag(F2FS_I(inode), FI_DIRTY_INODE); +} + +static void f2fs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); +} + +static void f2fs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, f2fs_i_callback); +} + +static void f2fs_put_super(struct super_block *sb) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + if (sbi->s_proc) { + remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry(sb->s_id, f2fs_proc_root); + } + kobject_del(&sbi->s_kobj); + + f2fs_destroy_stats(sbi); + stop_gc_thread(sbi); + + /* + * We don't need to do checkpoint when superblock is clean. + * But, the previous checkpoint was not done by umount, it needs to do + * clean checkpoint again. + */ + if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || + !is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) { + struct cp_control cpc = { + .reason = CP_UMOUNT, + }; + write_checkpoint(sbi, &cpc); + } + + /* + * normally superblock is clean, so we need to release this. + * In addition, EIO will skip do checkpoint, we need this as well. + */ + release_dirty_inode(sbi); + release_discard_addrs(sbi); + + iput(sbi->node_inode); + iput(sbi->meta_inode); + + /* destroy f2fs internal modules */ + destroy_node_manager(sbi); + destroy_segment_manager(sbi); + + kfree(sbi->ckpt); + kobject_put(&sbi->s_kobj); + wait_for_completion(&sbi->s_kobj_unregister); + + sb->s_fs_info = NULL; + brelse(sbi->raw_super_buf); + kfree(sbi); +} + +int f2fs_sync_fs(struct super_block *sb, int sync) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + + trace_f2fs_sync_fs(sb, sync); + + if (sync) { + struct cp_control cpc; + + cpc.reason = __get_cp_reason(sbi); + + mutex_lock(&sbi->gc_mutex); + write_checkpoint(sbi, &cpc); + mutex_unlock(&sbi->gc_mutex); + } else { + f2fs_balance_fs(sbi); + } + f2fs_trace_ios(NULL, NULL, 1); + + return 0; +} + +static int f2fs_freeze(struct super_block *sb) +{ + int err; + + if (f2fs_readonly(sb)) + return 0; + + err = f2fs_sync_fs(sb, 1); + return err; +} + +static int f2fs_unfreeze(struct super_block *sb) +{ + return 0; +} + +static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + block_t total_count, user_block_count, start_count, ovp_count; + + total_count = le64_to_cpu(sbi->raw_super->block_count); + user_block_count = sbi->user_block_count; + start_count = le32_to_cpu(sbi->raw_super->segment0_blkaddr); + ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; + buf->f_type = F2FS_SUPER_MAGIC; + buf->f_bsize = sbi->blocksize; + + buf->f_blocks = total_count - start_count; + buf->f_bfree = buf->f_blocks - valid_user_blocks(sbi) - ovp_count; + buf->f_bavail = user_block_count - valid_user_blocks(sbi); + + buf->f_files = sbi->total_node_count - F2FS_RESERVED_NODE_NUM; + buf->f_ffree = buf->f_files - valid_inode_count(sbi); + + buf->f_namelen = F2FS_NAME_LEN; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + + return 0; +} + +static int f2fs_show_options(struct seq_file *seq, struct dentry *root) +{ + struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); + + if (!f2fs_readonly(sbi->sb) && test_opt(sbi, BG_GC)) + seq_printf(seq, ",background_gc=%s", "on"); + else + seq_printf(seq, ",background_gc=%s", "off"); + if (test_opt(sbi, DISABLE_ROLL_FORWARD)) + seq_puts(seq, ",disable_roll_forward"); + if (test_opt(sbi, DISCARD)) + seq_puts(seq, ",discard"); + if (test_opt(sbi, NOHEAP)) + seq_puts(seq, ",no_heap_alloc"); +#ifdef CONFIG_F2FS_FS_XATTR + if (test_opt(sbi, XATTR_USER)) + seq_puts(seq, ",user_xattr"); + else + seq_puts(seq, ",nouser_xattr"); + if (test_opt(sbi, INLINE_XATTR)) + seq_puts(seq, ",inline_xattr"); +#endif +#ifdef CONFIG_F2FS_FS_POSIX_ACL + if (test_opt(sbi, POSIX_ACL)) + seq_puts(seq, ",acl"); + else + seq_puts(seq, ",noacl"); +#endif + if (test_opt(sbi, DISABLE_EXT_IDENTIFY)) + seq_puts(seq, ",disable_ext_identify"); + if (test_opt(sbi, INLINE_DATA)) + seq_puts(seq, ",inline_data"); + else + seq_puts(seq, ",noinline_data"); + if (test_opt(sbi, INLINE_DENTRY)) + seq_puts(seq, ",inline_dentry"); + if (!f2fs_readonly(sbi->sb) && test_opt(sbi, FLUSH_MERGE)) + seq_puts(seq, ",flush_merge"); + if (test_opt(sbi, NOBARRIER)) + seq_puts(seq, ",nobarrier"); + if (test_opt(sbi, FASTBOOT)) + seq_puts(seq, ",fastboot"); + if (test_opt(sbi, EXTENT_CACHE)) + seq_puts(seq, ",extent_cache"); + seq_printf(seq, ",active_logs=%u", sbi->active_logs); + + return 0; +} + +static int segment_info_seq_show(struct seq_file *seq, void *offset) +{ + struct super_block *sb = seq->private; + struct f2fs_sb_info *sbi = F2FS_SB(sb); + unsigned int total_segs = + le32_to_cpu(sbi->raw_super->segment_count_main); + int i; + + seq_puts(seq, "format: segment_type|valid_blocks\n" + "segment_type(0:HD, 1:WD, 2:CD, 3:HN, 4:WN, 5:CN)\n"); + + for (i = 0; i < total_segs; i++) { + struct seg_entry *se = get_seg_entry(sbi, i); + + if ((i % 10) == 0) + seq_printf(seq, "%-5d", i); + seq_printf(seq, "%d|%-3u", se->type, + get_valid_blocks(sbi, i, 1)); + if ((i % 10) == 9 || i == (total_segs - 1)) + seq_putc(seq, '\n'); + else + seq_putc(seq, ' '); + } + + return 0; +} + +static int segment_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, segment_info_seq_show, PDE(inode)->data); +} + +static const struct file_operations f2fs_seq_segment_info_fops = { + .owner = THIS_MODULE, + .open = segment_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int f2fs_remount(struct super_block *sb, int *flags, char *data) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct f2fs_mount_info org_mount_opt; + int err, active_logs; + bool need_restart_gc = false; + bool need_stop_gc = false; + + sync_filesystem(sb); + + /* + * Save the old mount options in case we + * need to restore them. + */ + org_mount_opt = sbi->mount_opt; + active_logs = sbi->active_logs; + + sbi->mount_opt.opt = 0; + sbi->active_logs = NR_CURSEG_TYPE; + + /* parse mount options */ + err = parse_options(sb, data); + if (err) + goto restore_opts; + + /* + * Previous and new state of filesystem is RO, + * so skip checking GC and FLUSH_MERGE conditions. + */ + if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) + goto skip; + + /* + * We stop the GC thread if FS is mounted as RO + * or if background_gc = off is passed in mount + * option. Also sync the filesystem. + */ + if ((*flags & MS_RDONLY) || !test_opt(sbi, BG_GC)) { + if (sbi->gc_thread) { + stop_gc_thread(sbi); + f2fs_sync_fs(sb, 1); + need_restart_gc = true; + } + } else if (!sbi->gc_thread) { + err = start_gc_thread(sbi); + if (err) + goto restore_opts; + need_stop_gc = true; + } + + /* + * We stop issue flush thread if FS is mounted as RO + * or if flush_merge is not passed in mount option. + */ + if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { + destroy_flush_cmd_control(sbi); + } else if (!SM_I(sbi)->cmd_control_info) { + err = create_flush_cmd_control(sbi); + if (err) + goto restore_gc; + } +skip: + /* Update the POSIXACL Flag */ + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); + return 0; +restore_gc: + if (need_restart_gc) { + if (start_gc_thread(sbi)) + f2fs_msg(sbi->sb, KERN_WARNING, + "background gc thread has stopped"); + } else if (need_stop_gc) { + stop_gc_thread(sbi); + } +restore_opts: + sbi->mount_opt = org_mount_opt; + sbi->active_logs = active_logs; + return err; +} + +static struct super_operations f2fs_sops = { + .alloc_inode = f2fs_alloc_inode, + .drop_inode = f2fs_drop_inode, + .destroy_inode = f2fs_destroy_inode, + .write_inode = f2fs_write_inode, + .dirty_inode = f2fs_dirty_inode, + .show_options = f2fs_show_options, + .evict_inode = f2fs_evict_inode, + .put_super = f2fs_put_super, + .sync_fs = f2fs_sync_fs, + .freeze_fs = f2fs_freeze, + .unfreeze_fs = f2fs_unfreeze, + .statfs = f2fs_statfs, + .remount_fs = f2fs_remount, +}; + +static struct inode *f2fs_nfs_get_inode(struct super_block *sb, + u64 ino, u32 generation) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct inode *inode; + + if (check_nid_range(sbi, ino)) + return ERR_PTR(-ESTALE); + + /* + * f2fs_iget isn't quite right if the inode is currently unallocated! + * However f2fs_iget currently does appropriate checks to handle stale + * inodes so everything is OK. + */ + inode = f2fs_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (unlikely(generation && inode->i_generation != generation)) { + /* we didn't find the right inode.. */ + iput(inode); + return ERR_PTR(-ESTALE); + } + return inode; +} + +static struct dentry *f2fs_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + f2fs_nfs_get_inode); +} + +static struct dentry *f2fs_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + f2fs_nfs_get_inode); +} + +static const struct export_operations f2fs_export_ops = { + .fh_to_dentry = f2fs_fh_to_dentry, + .fh_to_parent = f2fs_fh_to_parent, + .get_parent = f2fs_get_parent, +}; + +static loff_t max_file_size(unsigned bits) +{ + loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); + loff_t leaf_count = ADDRS_PER_BLOCK; + + /* two direct node blocks */ + result += (leaf_count * 2); + + /* two indirect node blocks */ + leaf_count *= NIDS_PER_BLOCK; + result += (leaf_count * 2); + + /* one double indirect node block */ + leaf_count *= NIDS_PER_BLOCK; + result += leaf_count; + + result <<= bits; + return result; +} + +static int sanity_check_raw_super(struct super_block *sb, + struct f2fs_super_block *raw_super) +{ + unsigned int blocksize; + + if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) { + f2fs_msg(sb, KERN_INFO, + "Magic Mismatch, valid(0x%x) - read(0x%x)", + F2FS_SUPER_MAGIC, le32_to_cpu(raw_super->magic)); + return 1; + } + + /* Currently, support only 4KB page cache size */ + if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid page_cache_size (%lu), supports only 4KB\n", + PAGE_CACHE_SIZE); + return 1; + } + + /* Currently, support only 4KB block size */ + blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); + if (blocksize != F2FS_BLKSIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid blocksize (%u), supports only 4KB\n", + blocksize); + return 1; + } + + /* Currently, support 512/1024/2048/4096 bytes sector size */ + if (le32_to_cpu(raw_super->log_sectorsize) > + F2FS_MAX_LOG_SECTOR_SIZE || + le32_to_cpu(raw_super->log_sectorsize) < + F2FS_MIN_LOG_SECTOR_SIZE) { + f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize (%u)", + le32_to_cpu(raw_super->log_sectorsize)); + return 1; + } + if (le32_to_cpu(raw_super->log_sectors_per_block) + + le32_to_cpu(raw_super->log_sectorsize) != + F2FS_MAX_LOG_SECTOR_SIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid log sectors per block(%u) log sectorsize(%u)", + le32_to_cpu(raw_super->log_sectors_per_block), + le32_to_cpu(raw_super->log_sectorsize)); + return 1; + } + return 0; +} + +static int sanity_check_ckpt(struct f2fs_sb_info *sbi) +{ + unsigned int total, fsmeta; + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); + + total = le32_to_cpu(raw_super->segment_count); + fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); + fsmeta += le32_to_cpu(raw_super->segment_count_sit); + fsmeta += le32_to_cpu(raw_super->segment_count_nat); + fsmeta += le32_to_cpu(ckpt->rsvd_segment_count); + fsmeta += le32_to_cpu(raw_super->segment_count_ssa); + + if (unlikely(fsmeta >= total)) + return 1; + + if (unlikely(f2fs_cp_error(sbi))) { + f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck"); + return 1; + } + return 0; +} + +static void init_sb_info(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *raw_super = sbi->raw_super; + int i; + + sbi->log_sectors_per_block = + le32_to_cpu(raw_super->log_sectors_per_block); + sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize); + sbi->blocksize = 1 << sbi->log_blocksize; + sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); + sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg; + sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec); + sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone); + sbi->total_sections = le32_to_cpu(raw_super->section_count); + sbi->total_node_count = + (le32_to_cpu(raw_super->segment_count_nat) / 2) + * sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK; + sbi->root_ino_num = le32_to_cpu(raw_super->root_ino); + sbi->node_ino_num = le32_to_cpu(raw_super->node_ino); + sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino); + sbi->cur_victim_sec = NULL_SECNO; + sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH; + + for (i = 0; i < NR_COUNT_TYPE; i++) + atomic_set(&sbi->nr_pages[i], 0); + + sbi->dir_level = DEF_DIR_LEVEL; + clear_sbi_flag(sbi, SBI_NEED_FSCK); +} + +/* + * Read f2fs raw super block. + * Because we have two copies of super block, so read the first one at first, + * if the first one is invalid, move to read the second one. + */ +static int read_raw_super_block(struct super_block *sb, + struct f2fs_super_block **raw_super, + struct buffer_head **raw_super_buf) +{ + int block = 0; + +retry: + *raw_super_buf = sb_bread(sb, block); + if (!*raw_super_buf) { + f2fs_msg(sb, KERN_ERR, "Unable to read %dth superblock", + block + 1); + if (block == 0) { + block++; + goto retry; + } else { + return -EIO; + } + } + + *raw_super = (struct f2fs_super_block *) + ((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET); + + /* sanity checking of raw super */ + if (sanity_check_raw_super(sb, *raw_super)) { + brelse(*raw_super_buf); + f2fs_msg(sb, KERN_ERR, + "Can't find valid F2FS filesystem in %dth superblock", + block + 1); + if (block == 0) { + block++; + goto retry; + } else { + return -EINVAL; + } + } + + return 0; +} + +static int f2fs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct f2fs_sb_info *sbi; + struct f2fs_super_block *raw_super = NULL; + struct buffer_head *raw_super_buf; + struct inode *root; + long err = -EINVAL; + bool retry = true, need_fsck = false; + char *options = NULL; + int i; + +try_onemore: + /* allocate memory for f2fs-specific super block info */ + sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + + /* set a block size */ + if (unlikely(!sb_set_blocksize(sb, F2FS_BLKSIZE))) { + f2fs_msg(sb, KERN_ERR, "unable to set blocksize"); + goto free_sbi; + } + + err = read_raw_super_block(sb, &raw_super, &raw_super_buf); + if (err) + goto free_sbi; + + sb->s_fs_info = sbi; + /* init some FS parameters */ + sbi->active_logs = NR_CURSEG_TYPE; + + set_opt(sbi, BG_GC); + set_opt(sbi, INLINE_DATA); + +#ifdef CONFIG_F2FS_FS_XATTR + set_opt(sbi, XATTR_USER); +#endif +#ifdef CONFIG_F2FS_FS_POSIX_ACL + set_opt(sbi, POSIX_ACL); +#endif + /* parse mount options */ + options = kstrdup((const char *)data, GFP_KERNEL); + if (data && !options) { + err = -ENOMEM; + goto free_sb_buf; + } + + err = parse_options(sb, options); + if (err) + goto free_options; + + sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); + sb->s_max_links = F2FS_LINK_MAX; + get_random_bytes(&sbi->s_next_generation, sizeof(u32)); + + sb->s_op = &f2fs_sops; + sb->s_xattr = f2fs_xattr_handlers; + sb->s_export_op = &f2fs_export_ops; + sb->s_magic = F2FS_SUPER_MAGIC; + sb->s_time_gran = 1; + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | + (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); + memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); + + /* init f2fs-specific super block info */ + sbi->sb = sb; + sbi->raw_super = raw_super; + sbi->raw_super_buf = raw_super_buf; + mutex_init(&sbi->gc_mutex); + mutex_init(&sbi->cp_mutex); + init_rwsem(&sbi->node_write); + clear_sbi_flag(sbi, SBI_POR_DOING); + spin_lock_init(&sbi->stat_lock); + + init_rwsem(&sbi->read_io.io_rwsem); + sbi->read_io.sbi = sbi; + sbi->read_io.bio = NULL; + for (i = 0; i < NR_PAGE_TYPE; i++) { + init_rwsem(&sbi->write_io[i].io_rwsem); + sbi->write_io[i].sbi = sbi; + sbi->write_io[i].bio = NULL; + } + + init_rwsem(&sbi->cp_rwsem); + init_waitqueue_head(&sbi->cp_wait); + init_sb_info(sbi); + + /* get an inode for meta space */ + sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi)); + if (IS_ERR(sbi->meta_inode)) { + f2fs_msg(sb, KERN_ERR, "Failed to read F2FS meta data inode"); + err = PTR_ERR(sbi->meta_inode); + goto free_options; + } + + err = get_valid_checkpoint(sbi); + if (err) { + f2fs_msg(sb, KERN_ERR, "Failed to get valid F2FS checkpoint"); + goto free_meta_inode; + } + + /* sanity checking of checkpoint */ + err = -EINVAL; + if (sanity_check_ckpt(sbi)) { + f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint"); + goto free_cp; + } + + sbi->total_valid_node_count = + le32_to_cpu(sbi->ckpt->valid_node_count); + sbi->total_valid_inode_count = + le32_to_cpu(sbi->ckpt->valid_inode_count); + sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count); + sbi->total_valid_block_count = + le64_to_cpu(sbi->ckpt->valid_block_count); + sbi->last_valid_block_count = sbi->total_valid_block_count; + sbi->alloc_valid_block_count = 0; + INIT_LIST_HEAD(&sbi->dir_inode_list); + spin_lock_init(&sbi->dir_inode_lock); + + init_extent_cache_info(sbi); + + init_ino_entry_info(sbi); + + /* setup f2fs internal modules */ + err = build_segment_manager(sbi); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to initialize F2FS segment manager"); + goto free_sm; + } + err = build_node_manager(sbi); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to initialize F2FS node manager"); + goto free_nm; + } + + build_gc_manager(sbi); + + /* get an inode for node space */ + sbi->node_inode = f2fs_iget(sb, F2FS_NODE_INO(sbi)); + if (IS_ERR(sbi->node_inode)) { + f2fs_msg(sb, KERN_ERR, "Failed to read node inode"); + err = PTR_ERR(sbi->node_inode); + goto free_nm; + } + + /* if there are nt orphan nodes free them */ + recover_orphan_inodes(sbi); + + /* read root inode and dentry */ + root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); + if (IS_ERR(root)) { + f2fs_msg(sb, KERN_ERR, "Failed to read root inode"); + err = PTR_ERR(root); + goto free_node_inode; + } + if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { + iput(root); + err = -EINVAL; + goto free_node_inode; + } + + sb->s_root = d_make_root(root); /* allocate root dentry */ + if (!sb->s_root) { + err = -ENOMEM; + goto free_root_inode; + } + + err = f2fs_build_stats(sbi); + if (err) + goto free_root_inode; + + if (f2fs_proc_root) + sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); + + if (sbi->s_proc) + proc_create_data("segment_info", S_IRUGO, sbi->s_proc, + &f2fs_seq_segment_info_fops, sb); + + if (test_opt(sbi, DISCARD)) { + struct request_queue *q = bdev_get_queue(sb->s_bdev); + if (!blk_queue_discard(q)) + f2fs_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but " + "the device does not support discard"); + } + + sbi->s_kobj.kset = f2fs_kset; + init_completion(&sbi->s_kobj_unregister); + err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, + "%s", sb->s_id); + if (err) + goto free_proc; + + /* recover fsynced data */ + if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { + /* + * mount should be failed, when device has readonly mode, and + * previous checkpoint was not done by clean system shutdown. + */ + if (bdev_read_only(sb->s_bdev) && + !is_set_ckpt_flags(sbi->ckpt, CP_UMOUNT_FLAG)) { + err = -EROFS; + goto free_kobj; + } + + if (need_fsck) + set_sbi_flag(sbi, SBI_NEED_FSCK); + + err = recover_fsync_data(sbi); + if (err) { + need_fsck = true; + f2fs_msg(sb, KERN_ERR, + "Cannot recover all fsync data errno=%ld", err); + goto free_kobj; + } + } + + /* + * If filesystem is not mounted as read-only then + * do start the gc_thread. + */ + if (test_opt(sbi, BG_GC) && !f2fs_readonly(sb)) { + /* After POR, we can run background GC thread.*/ + err = start_gc_thread(sbi); + if (err) + goto free_kobj; + } + kfree(options); + return 0; + +free_kobj: + kobject_del(&sbi->s_kobj); +free_proc: + if (sbi->s_proc) { + remove_proc_entry("segment_info", sbi->s_proc); + remove_proc_entry(sb->s_id, f2fs_proc_root); + } + f2fs_destroy_stats(sbi); +free_root_inode: + dput(sb->s_root); + sb->s_root = NULL; +free_node_inode: + iput(sbi->node_inode); +free_nm: + destroy_node_manager(sbi); +free_sm: + destroy_segment_manager(sbi); +free_cp: + kfree(sbi->ckpt); +free_meta_inode: + make_bad_inode(sbi->meta_inode); + iput(sbi->meta_inode); +free_options: + kfree(options); +free_sb_buf: + brelse(raw_super_buf); +free_sbi: + kfree(sbi); + + /* give only one another chance */ + if (retry) { + retry = false; + shrink_dcache_sb(sb); + goto try_onemore; + } + return err; +} + +static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super); +} + +static void kill_f2fs_super(struct super_block *sb) +{ + if (sb->s_root) + set_sbi_flag(F2FS_SB(sb), SBI_IS_CLOSE); + kill_block_super(sb); +} + +static struct file_system_type f2fs_fs_type = { + .owner = THIS_MODULE, + .name = "f2fs", + .mount = f2fs_mount, + .kill_sb = kill_f2fs_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_inodecache(void) +{ + f2fs_inode_cachep = f2fs_kmem_cache_create("f2fs_inode_cache", + sizeof(struct f2fs_inode_info)); + if (!f2fs_inode_cachep) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + /* + * Make sure all delayed rcu free inodes are flushed before we + * destroy cache. + */ + rcu_barrier(); + kmem_cache_destroy(f2fs_inode_cachep); +} + +static int __init init_f2fs_fs(void) +{ + int err; + + f2fs_build_trace_ios(); + + err = init_inodecache(); + if (err) + goto fail; + err = create_node_manager_caches(); + if (err) + goto free_inodecache; + err = create_segment_manager_caches(); + if (err) + goto free_node_manager_caches; + err = create_checkpoint_caches(); + if (err) + goto free_segment_manager_caches; + err = create_extent_cache(); + if (err) + goto free_checkpoint_caches; + f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); + if (!f2fs_kset) { + err = -ENOMEM; + goto free_extent_cache; + } + err = register_filesystem(&f2fs_fs_type); + if (err) + goto free_kset; + f2fs_create_root_stats(); + f2fs_proc_root = proc_mkdir("fs/f2fs", NULL); + return 0; + +free_kset: + kset_unregister(f2fs_kset); +free_extent_cache: + destroy_extent_cache(); +free_checkpoint_caches: + destroy_checkpoint_caches(); +free_segment_manager_caches: + destroy_segment_manager_caches(); +free_node_manager_caches: + destroy_node_manager_caches(); +free_inodecache: + destroy_inodecache(); +fail: + return err; +} + +static void __exit exit_f2fs_fs(void) +{ + remove_proc_entry("fs/f2fs", NULL); + f2fs_destroy_root_stats(); + unregister_filesystem(&f2fs_fs_type); + destroy_extent_cache(); + destroy_checkpoint_caches(); + destroy_segment_manager_caches(); + destroy_node_manager_caches(); + destroy_inodecache(); + kset_unregister(f2fs_kset); + f2fs_destroy_trace_ios(); +} + +module_init(init_f2fs_fs) +module_exit(exit_f2fs_fs) + +MODULE_AUTHOR("Samsung Electronics's Praesto Team"); +MODULE_DESCRIPTION("Flash Friendly File System"); +MODULE_LICENSE("GPL"); diff --git a/fs/f2fs/trace.c b/fs/f2fs/trace.c new file mode 100644 index 000000000000..875aa8179bc1 --- /dev/null +++ b/fs/f2fs/trace.c @@ -0,0 +1,159 @@ +/* + * f2fs IO tracer + * + * Copyright (c) 2014 Motorola Mobility + * Copyright (c) 2014 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include "f2fs.h" +#include "trace.h" + +static RADIX_TREE(pids, GFP_ATOMIC); +static spinlock_t pids_lock; +static struct last_io_info last_io; + +static inline void __print_last_io(void) +{ + if (!last_io.len) + return; + + trace_printk("%3x:%3x %4x %-16s %2x %5x %12x %4x\n", + last_io.major, last_io.minor, + last_io.pid, "----------------", + last_io.type, + last_io.fio.rw, last_io.fio.blk_addr, + last_io.len); + memset(&last_io, 0, sizeof(last_io)); +} + +static int __file_type(struct inode *inode, pid_t pid) +{ + if (f2fs_is_atomic_file(inode)) + return __ATOMIC_FILE; + else if (f2fs_is_volatile_file(inode)) + return __VOLATILE_FILE; + else if (S_ISDIR(inode->i_mode)) + return __DIR_FILE; + else if (inode->i_ino == F2FS_NODE_INO(F2FS_I_SB(inode))) + return __NODE_FILE; + else if (inode->i_ino == F2FS_META_INO(F2FS_I_SB(inode))) + return __META_FILE; + else if (pid) + return __NORMAL_FILE; + else + return __MISC_FILE; +} + +void f2fs_trace_pid(struct page *page) +{ + struct inode *inode = page->mapping->host; + pid_t pid = task_pid_nr(current); + void *p; + + page->private = pid; + + if (radix_tree_preload(GFP_NOFS)) + return; + + spin_lock(&pids_lock); + p = radix_tree_lookup(&pids, pid); + if (p == current) + goto out; + if (p) + radix_tree_delete(&pids, pid); + + f2fs_radix_tree_insert(&pids, pid, current); + + trace_printk("%3x:%3x %4x %-16s\n", + MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), + pid, current->comm); +out: + spin_unlock(&pids_lock); + radix_tree_preload_end(); +} + +void f2fs_trace_ios(struct page *page, struct f2fs_io_info *fio, int flush) +{ + struct inode *inode; + pid_t pid; + int major, minor; + + if (flush) { + __print_last_io(); + return; + } + + inode = page->mapping->host; + pid = page_private(page); + + major = MAJOR(inode->i_sb->s_dev); + minor = MINOR(inode->i_sb->s_dev); + + if (last_io.major == major && last_io.minor == minor && + last_io.pid == pid && + last_io.type == __file_type(inode, pid) && + last_io.fio.rw == fio->rw && + last_io.fio.blk_addr + last_io.len == fio->blk_addr) { + last_io.len++; + return; + } + + __print_last_io(); + + last_io.major = major; + last_io.minor = minor; + last_io.pid = pid; + last_io.type = __file_type(inode, pid); + last_io.fio = *fio; + last_io.len = 1; + return; +} + +void f2fs_build_trace_ios(void) +{ + spin_lock_init(&pids_lock); +} + +#define PIDVEC_SIZE 128 +static unsigned int gang_lookup_pids(pid_t *results, unsigned long first_index, + unsigned int max_items) +{ + struct radix_tree_iter iter; + void **slot; + unsigned int ret = 0; + + if (unlikely(!max_items)) + return 0; + + radix_tree_for_each_slot(slot, &pids, &iter, first_index) { + results[ret] = iter.index; + if (++ret == PIDVEC_SIZE) + break; + } + return ret; +} + +void f2fs_destroy_trace_ios(void) +{ + pid_t pid[PIDVEC_SIZE]; + pid_t next_pid = 0; + unsigned int found; + + spin_lock(&pids_lock); + while ((found = gang_lookup_pids(pid, next_pid, PIDVEC_SIZE))) { + unsigned idx; + + next_pid = pid[found - 1] + 1; + for (idx = 0; idx < found; idx++) + radix_tree_delete(&pids, pid[idx]); + } + spin_unlock(&pids_lock); +} diff --git a/fs/f2fs/trace.h b/fs/f2fs/trace.h new file mode 100644 index 000000000000..1041dbeb52ae --- /dev/null +++ b/fs/f2fs/trace.h @@ -0,0 +1,46 @@ +/* + * f2fs IO tracer + * + * Copyright (c) 2014 Motorola Mobility + * Copyright (c) 2014 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __F2FS_TRACE_H__ +#define __F2FS_TRACE_H__ + +#ifdef CONFIG_F2FS_IO_TRACE +#include + +enum file_type { + __NORMAL_FILE, + __DIR_FILE, + __NODE_FILE, + __META_FILE, + __ATOMIC_FILE, + __VOLATILE_FILE, + __MISC_FILE, +}; + +struct last_io_info { + int major, minor; + pid_t pid; + enum file_type type; + struct f2fs_io_info fio; + block_t len; +}; + +extern void f2fs_trace_pid(struct page *); +extern void f2fs_trace_ios(struct page *, struct f2fs_io_info *, int); +extern void f2fs_build_trace_ios(void); +extern void f2fs_destroy_trace_ios(void); +#else +#define f2fs_trace_pid(p) +#define f2fs_trace_ios(p, i, n) +#define f2fs_build_trace_ios() +#define f2fs_destroy_trace_ios() + +#endif +#endif /* __F2FS_TRACE_H__ */ diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c new file mode 100644 index 000000000000..0569d5f8b552 --- /dev/null +++ b/fs/f2fs/xattr.c @@ -0,0 +1,617 @@ +/* + * fs/f2fs/xattr.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Portions of this code from linux/fs/ext2/xattr.c + * + * Copyright (C) 2001-2003 Andreas Gruenbacher + * + * Fix by Harrison Xing . + * Extended attributes for symlinks and special files added per + * suggestion of Luka Renko . + * xattr consolidation Copyright (c) 2004 James Morris , + * Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include "f2fs.h" +#include "xattr.h" + +static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list, + size_t list_size, const char *name, size_t len, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + int total_len, prefix_len = 0; + const char *prefix = NULL; + + switch (type) { + case F2FS_XATTR_INDEX_USER: + if (!test_opt(sbi, XATTR_USER)) + return -EOPNOTSUPP; + prefix = XATTR_USER_PREFIX; + prefix_len = XATTR_USER_PREFIX_LEN; + break; + case F2FS_XATTR_INDEX_TRUSTED: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + prefix = XATTR_TRUSTED_PREFIX; + prefix_len = XATTR_TRUSTED_PREFIX_LEN; + break; + case F2FS_XATTR_INDEX_SECURITY: + prefix = XATTR_SECURITY_PREFIX; + prefix_len = XATTR_SECURITY_PREFIX_LEN; + break; + default: + return -EINVAL; + } + + total_len = prefix_len + len + 1; + if (list && total_len <= list_size) { + memcpy(list, prefix, prefix_len); + memcpy(list + prefix_len, name, len); + list[prefix_len + len] = '\0'; + } + return total_len; +} + +static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + + switch (type) { + case F2FS_XATTR_INDEX_USER: + if (!test_opt(sbi, XATTR_USER)) + return -EOPNOTSUPP; + break; + case F2FS_XATTR_INDEX_TRUSTED: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + break; + case F2FS_XATTR_INDEX_SECURITY: + break; + default: + return -EINVAL; + } + if (strcmp(name, "") == 0) + return -EINVAL; + return f2fs_getxattr(dentry->d_inode, type, name, buffer, size, NULL); +} + +static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); + + switch (type) { + case F2FS_XATTR_INDEX_USER: + if (!test_opt(sbi, XATTR_USER)) + return -EOPNOTSUPP; + break; + case F2FS_XATTR_INDEX_TRUSTED: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + break; + case F2FS_XATTR_INDEX_SECURITY: + break; + default: + return -EINVAL; + } + if (strcmp(name, "") == 0) + return -EINVAL; + + return f2fs_setxattr(dentry->d_inode, type, name, + value, size, NULL, flags); +} + +static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list, + size_t list_size, const char *name, size_t len, int type) +{ + const char *xname = F2FS_SYSTEM_ADVISE_PREFIX; + size_t size; + + if (type != F2FS_XATTR_INDEX_ADVISE) + return 0; + + size = strlen(xname) + 1; + if (list && size <= list_size) + memcpy(list, xname, size); + return size; +} + +static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + struct inode *inode = dentry->d_inode; + + if (strcmp(name, "") != 0) + return -EINVAL; + + if (buffer) + *((char *)buffer) = F2FS_I(inode)->i_advise; + return sizeof(char); +} + +static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + struct inode *inode = dentry->d_inode; + + if (strcmp(name, "") != 0) + return -EINVAL; + if (!inode_owner_or_capable(inode)) + return -EPERM; + if (value == NULL) + return -EINVAL; + + F2FS_I(inode)->i_advise |= *(char *)value; + mark_inode_dirty(inode); + return 0; +} + +#ifdef CONFIG_F2FS_FS_SECURITY +static int f2fs_initxattrs(struct inode *inode, const struct xattr *xattr_array, + void *page) +{ + const struct xattr *xattr; + int err = 0; + + for (xattr = xattr_array; xattr->name != NULL; xattr++) { + err = f2fs_setxattr(inode, F2FS_XATTR_INDEX_SECURITY, + xattr->name, xattr->value, + xattr->value_len, (struct page *)page, 0); + if (err < 0) + break; + } + return err; +} + +int f2fs_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr, struct page *ipage) +{ + return security_inode_init_security(inode, dir, qstr, + &f2fs_initxattrs, ipage); +} +#endif + +const struct xattr_handler f2fs_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, + .flags = F2FS_XATTR_INDEX_USER, + .list = f2fs_xattr_generic_list, + .get = f2fs_xattr_generic_get, + .set = f2fs_xattr_generic_set, +}; + +const struct xattr_handler f2fs_xattr_trusted_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .flags = F2FS_XATTR_INDEX_TRUSTED, + .list = f2fs_xattr_generic_list, + .get = f2fs_xattr_generic_get, + .set = f2fs_xattr_generic_set, +}; + +const struct xattr_handler f2fs_xattr_advise_handler = { + .prefix = F2FS_SYSTEM_ADVISE_PREFIX, + .flags = F2FS_XATTR_INDEX_ADVISE, + .list = f2fs_xattr_advise_list, + .get = f2fs_xattr_advise_get, + .set = f2fs_xattr_advise_set, +}; + +const struct xattr_handler f2fs_xattr_security_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .flags = F2FS_XATTR_INDEX_SECURITY, + .list = f2fs_xattr_generic_list, + .get = f2fs_xattr_generic_get, + .set = f2fs_xattr_generic_set, +}; + +static const struct xattr_handler *f2fs_xattr_handler_map[] = { + [F2FS_XATTR_INDEX_USER] = &f2fs_xattr_user_handler, +#ifdef CONFIG_F2FS_FS_POSIX_ACL + [F2FS_XATTR_INDEX_POSIX_ACL_ACCESS] = &f2fs_xattr_acl_access_handler, + [F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &f2fs_xattr_acl_default_handler, +#endif + [F2FS_XATTR_INDEX_TRUSTED] = &f2fs_xattr_trusted_handler, +#ifdef CONFIG_F2FS_FS_SECURITY + [F2FS_XATTR_INDEX_SECURITY] = &f2fs_xattr_security_handler, +#endif + [F2FS_XATTR_INDEX_ADVISE] = &f2fs_xattr_advise_handler, +}; + +const struct xattr_handler *f2fs_xattr_handlers[] = { + &f2fs_xattr_user_handler, +#ifdef CONFIG_F2FS_FS_POSIX_ACL + &f2fs_xattr_acl_access_handler, + &f2fs_xattr_acl_default_handler, +#endif + &f2fs_xattr_trusted_handler, +#ifdef CONFIG_F2FS_FS_SECURITY + &f2fs_xattr_security_handler, +#endif + &f2fs_xattr_advise_handler, + NULL, +}; + +static inline const struct xattr_handler *f2fs_xattr_handler(int index) +{ + const struct xattr_handler *handler = NULL; + + if (index > 0 && index < ARRAY_SIZE(f2fs_xattr_handler_map)) + handler = f2fs_xattr_handler_map[index]; + return handler; +} + +static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, + size_t len, const char *name) +{ + struct f2fs_xattr_entry *entry; + + list_for_each_xattr(entry, base_addr) { + if (entry->e_name_index != index) + continue; + if (entry->e_name_len != len) + continue; + if (!memcmp(entry->e_name, name, len)) + break; + } + return entry; +} + +static void *read_all_xattrs(struct inode *inode, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct f2fs_xattr_header *header; + size_t size = PAGE_SIZE, inline_size = 0; + void *txattr_addr; + + inline_size = inline_xattr_size(inode); + + txattr_addr = kzalloc(inline_size + size, GFP_F2FS_ZERO); + if (!txattr_addr) + return NULL; + + /* read from inline xattr */ + if (inline_size) { + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) + goto fail; + inline_addr = inline_xattr_addr(page); + } + memcpy(txattr_addr, inline_addr, inline_size); + f2fs_put_page(page, 1); + } + + /* read from xattr node block */ + if (F2FS_I(inode)->i_xattr_nid) { + struct page *xpage; + void *xattr_addr; + + /* The inode already has an extended attribute block. */ + xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); + if (IS_ERR(xpage)) + goto fail; + + xattr_addr = page_address(xpage); + memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE); + f2fs_put_page(xpage, 1); + } + + header = XATTR_HDR(txattr_addr); + + /* never been allocated xattrs */ + if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) { + header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); + header->h_refcount = cpu_to_le32(1); + } + return txattr_addr; +fail: + kzfree(txattr_addr); + return NULL; +} + +static inline int write_all_xattrs(struct inode *inode, __u32 hsize, + void *txattr_addr, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + size_t inline_size = 0; + void *xattr_addr; + struct page *xpage; + nid_t new_nid = 0; + int err; + + inline_size = inline_xattr_size(inode); + + if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid) + if (!alloc_nid(sbi, &new_nid)) + return -ENOSPC; + + /* write to inline xattr */ + if (inline_size) { + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + f2fs_wait_on_page_writeback(ipage, NODE); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(page); + } + inline_addr = inline_xattr_addr(page); + f2fs_wait_on_page_writeback(page, NODE); + } + memcpy(inline_addr, txattr_addr, inline_size); + f2fs_put_page(page, 1); + + /* no need to use xattr node block */ + if (hsize <= inline_size) { + err = truncate_xattr_node(inode, ipage); + alloc_nid_failed(sbi, new_nid); + return err; + } + } + + /* write to xattr node block */ + if (F2FS_I(inode)->i_xattr_nid) { + xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(xpage); + } + f2fs_bug_on(sbi, new_nid); + f2fs_wait_on_page_writeback(xpage, NODE); + } else { + struct dnode_of_data dn; + set_new_dnode(&dn, inode, NULL, NULL, new_nid); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(xpage); + } + alloc_nid_done(sbi, new_nid); + } + + xattr_addr = page_address(xpage); + memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE - + sizeof(struct node_footer)); + set_page_dirty(xpage); + f2fs_put_page(xpage, 1); + + /* need to checkpoint during fsync */ + F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); + return 0; +} + +int f2fs_getxattr(struct inode *inode, int index, const char *name, + void *buffer, size_t buffer_size, struct page *ipage) +{ + struct f2fs_xattr_entry *entry; + void *base_addr; + int error = 0; + size_t size, len; + + if (name == NULL) + return -EINVAL; + + len = strlen(name); + if (len > F2FS_NAME_LEN) + return -ERANGE; + + base_addr = read_all_xattrs(inode, ipage); + if (!base_addr) + return -ENOMEM; + + entry = __find_xattr(base_addr, index, len, name); + if (IS_XATTR_LAST_ENTRY(entry)) { + error = -ENODATA; + goto cleanup; + } + + size = le16_to_cpu(entry->e_value_size); + + if (buffer && size > buffer_size) { + error = -ERANGE; + goto cleanup; + } + + if (buffer) { + char *pval = entry->e_name + entry->e_name_len; + memcpy(buffer, pval, size); + } + error = size; + +cleanup: + kzfree(base_addr); + return error; +} + +ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) +{ + struct inode *inode = dentry->d_inode; + struct f2fs_xattr_entry *entry; + void *base_addr; + int error = 0; + size_t rest = buffer_size; + + base_addr = read_all_xattrs(inode, NULL); + if (!base_addr) + return -ENOMEM; + + list_for_each_xattr(entry, base_addr) { + const struct xattr_handler *handler = + f2fs_xattr_handler(entry->e_name_index); + size_t size; + + if (!handler) + continue; + + size = handler->list(dentry, buffer, rest, entry->e_name, + entry->e_name_len, handler->flags); + if (buffer && size > rest) { + error = -ERANGE; + goto cleanup; + } + + if (buffer) + buffer += size; + rest -= size; + } + error = buffer_size - rest; +cleanup: + kzfree(base_addr); + return error; +} + +static int __f2fs_setxattr(struct inode *inode, int index, + const char *name, const void *value, size_t size, + struct page *ipage, int flags) +{ + struct f2fs_inode_info *fi = F2FS_I(inode); + struct f2fs_xattr_entry *here, *last; + void *base_addr; + int found, newsize; + size_t len; + __u32 new_hsize; + int error = -ENOMEM; + + if (name == NULL) + return -EINVAL; + + if (value == NULL) + size = 0; + + len = strlen(name); + + if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN(inode)) + return -ERANGE; + + base_addr = read_all_xattrs(inode, ipage); + if (!base_addr) + goto exit; + + /* find entry with wanted name. */ + here = __find_xattr(base_addr, index, len, name); + + found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1; + + if ((flags & XATTR_REPLACE) && !found) { + error = -ENODATA; + goto exit; + } else if ((flags & XATTR_CREATE) && found) { + error = -EEXIST; + goto exit; + } + + last = here; + while (!IS_XATTR_LAST_ENTRY(last)) + last = XATTR_NEXT_ENTRY(last); + + newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size); + + /* 1. Check space */ + if (value) { + int free; + /* + * If value is NULL, it is remove operation. + * In case of update operation, we calculate free. + */ + free = MIN_OFFSET(inode) - ((char *)last - (char *)base_addr); + if (found) + free = free + ENTRY_SIZE(here); + + if (unlikely(free < newsize)) { + error = -ENOSPC; + goto exit; + } + } + + /* 2. Remove old entry */ + if (found) { + /* + * If entry is found, remove old entry. + * If not found, remove operation is not needed. + */ + struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here); + int oldsize = ENTRY_SIZE(here); + + memmove(here, next, (char *)last - (char *)next); + last = (struct f2fs_xattr_entry *)((char *)last - oldsize); + memset(last, 0, oldsize); + } + + new_hsize = (char *)last - (char *)base_addr; + + /* 3. Write new entry */ + if (value) { + char *pval; + /* + * Before we come here, old entry is removed. + * We just write new entry. + */ + memset(last, 0, newsize); + last->e_name_index = index; + last->e_name_len = len; + memcpy(last->e_name, name, len); + pval = last->e_name + len; + memcpy(pval, value, size); + last->e_value_size = cpu_to_le16(size); + new_hsize += newsize; + } + + error = write_all_xattrs(inode, new_hsize, base_addr, ipage); + if (error) + goto exit; + + if (is_inode_flag_set(fi, FI_ACL_MODE)) { + inode->i_mode = fi->i_acl_mode; + inode->i_ctime = CURRENT_TIME; + clear_inode_flag(fi, FI_ACL_MODE); + } + + if (ipage) + update_inode(inode, ipage); + else + update_inode_page(inode); +exit: + kzfree(base_addr); + return error; +} + +int f2fs_setxattr(struct inode *inode, int index, const char *name, + const void *value, size_t size, + struct page *ipage, int flags) +{ + struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + int err; + + /* this case is only from init_inode_metadata */ + if (ipage) + return __f2fs_setxattr(inode, index, name, value, + size, ipage, flags); + f2fs_balance_fs(sbi); + + f2fs_lock_op(sbi); + /* protect xattr_ver */ + down_write(&F2FS_I(inode)->i_sem); + err = __f2fs_setxattr(inode, index, name, value, size, ipage, flags); + up_write(&F2FS_I(inode)->i_sem); + f2fs_unlock_op(sbi); + + return err; +} diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h new file mode 100644 index 000000000000..95b55a0c55cd --- /dev/null +++ b/fs/f2fs/xattr.h @@ -0,0 +1,154 @@ +/* + * fs/f2fs/xattr.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Portions of this code from linux/fs/ext2/xattr.h + * + * On-disk format of extended attributes for the ext2 filesystem. + * + * (C) 2001 Andreas Gruenbacher, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __F2FS_XATTR_H__ +#define __F2FS_XATTR_H__ + +#include +#include + +/* Magic value in attribute blocks */ +#define F2FS_XATTR_MAGIC 0xF2F52011 + +/* Maximum number of references to one attribute block */ +#define F2FS_XATTR_REFCOUNT_MAX 1024 + +/* Name indexes */ +#define F2FS_SYSTEM_ADVISE_PREFIX "system.advise" +#define F2FS_XATTR_INDEX_USER 1 +#define F2FS_XATTR_INDEX_POSIX_ACL_ACCESS 2 +#define F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT 3 +#define F2FS_XATTR_INDEX_TRUSTED 4 +#define F2FS_XATTR_INDEX_LUSTRE 5 +#define F2FS_XATTR_INDEX_SECURITY 6 +#define F2FS_XATTR_INDEX_ADVISE 7 + +struct f2fs_xattr_header { + __le32 h_magic; /* magic number for identification */ + __le32 h_refcount; /* reference count */ + __u32 h_reserved[4]; /* zero right now */ +}; + +struct f2fs_xattr_entry { + __u8 e_name_index; + __u8 e_name_len; + __le16 e_value_size; /* size of attribute value */ + char e_name[0]; /* attribute name */ +}; + +#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr)) +#define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr)) +#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr) + 1)) +#define XATTR_ROUND (3) + +#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND) + +#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \ + entry->e_name_len + le16_to_cpu(entry->e_value_size))) + +#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *)((char *)(entry) +\ + ENTRY_SIZE(entry))) + +#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) + +#define list_for_each_xattr(entry, addr) \ + for (entry = XATTR_FIRST_ENTRY(addr);\ + !IS_XATTR_LAST_ENTRY(entry);\ + entry = XATTR_NEXT_ENTRY(entry)) + +#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + PAGE_SIZE - \ + sizeof(struct node_footer) - sizeof(__u32)) + +#define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \ + sizeof(struct f2fs_xattr_header) - \ + sizeof(struct f2fs_xattr_entry)) + +/* + * On-disk structure of f2fs_xattr + * We use inline xattrs space + 1 block for xattr. + * + * +--------------------+ + * | f2fs_xattr_header | + * | | + * +--------------------+ + * | f2fs_xattr_entry | + * | .e_name_index = 1 | + * | .e_name_len = 3 | + * | .e_value_size = 14 | + * | .e_name = "foo" | + * | "value_of_xattr" |<- value_offs = e_name + e_name_len + * +--------------------+ + * | f2fs_xattr_entry | + * | .e_name_index = 4 | + * | .e_name = "bar" | + * +--------------------+ + * | | + * | Free | + * | | + * +--------------------+<- MIN_OFFSET + * | node_footer | + * | (nid, ino, offset) | + * +--------------------+ + * + **/ + +#ifdef CONFIG_F2FS_FS_XATTR +extern const struct xattr_handler f2fs_xattr_user_handler; +extern const struct xattr_handler f2fs_xattr_trusted_handler; +extern const struct xattr_handler f2fs_xattr_acl_access_handler; +extern const struct xattr_handler f2fs_xattr_acl_default_handler; +extern const struct xattr_handler f2fs_xattr_advise_handler; +extern const struct xattr_handler f2fs_xattr_security_handler; + +extern const struct xattr_handler *f2fs_xattr_handlers[]; + +extern int f2fs_setxattr(struct inode *, int, const char *, + const void *, size_t, struct page *, int); +extern int f2fs_getxattr(struct inode *, int, const char *, void *, + size_t, struct page *); +extern ssize_t f2fs_listxattr(struct dentry *, char *, size_t); +#else + +#define f2fs_xattr_handlers NULL +static inline int f2fs_setxattr(struct inode *inode, int index, + const char *name, const void *value, size_t size, int flags) +{ + return -EOPNOTSUPP; +} +static inline int f2fs_getxattr(struct inode *inode, int index, + const char *name, void *buffer, + size_t buffer_size, struct page *dpage) +{ + return -EOPNOTSUPP; +} +static inline ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, + size_t buffer_size) +{ + return -EOPNOTSUPP; +} +#endif + +#ifdef CONFIG_F2FS_FS_SECURITY +extern int f2fs_init_security(struct inode *, struct inode *, + const struct qstr *, struct page *); +#else +static inline int f2fs_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr, struct page *ipage) +{ + return 0; +} +#endif +#endif /* __F2FS_XATTR_H__ */ diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h new file mode 100644 index 000000000000..591f8c3ef410 --- /dev/null +++ b/include/linux/f2fs_fs.h @@ -0,0 +1,476 @@ +/** + * include/linux/f2fs_fs.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _LINUX_F2FS_FS_H +#define _LINUX_F2FS_FS_H + +#include +#include + +#define F2FS_SUPER_OFFSET 1024 /* byte-size offset */ +#define F2FS_MIN_LOG_SECTOR_SIZE 9 /* 9 bits for 512 bytes */ +#define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */ +#define F2FS_LOG_SECTORS_PER_BLOCK 3 /* log number for sector/blk */ +#define F2FS_BLKSIZE 4096 /* support only 4KB block */ +#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */ +#define F2FS_MAX_EXTENSION 64 /* # of extension entries */ +#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) / F2FS_BLKSIZE) + +#define NULL_ADDR ((block_t)0) /* used as block_t addresses */ +#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ + +#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) +#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) + +/* 0, 1(node nid), 2(meta nid) are reserved node id */ +#define F2FS_RESERVED_NODE_NUM 3 + +#define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) +#define F2FS_NODE_INO(sbi) (sbi->node_ino_num) +#define F2FS_META_INO(sbi) (sbi->meta_ino_num) + +/* This flag is used by node and meta inodes, and by recovery */ +#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) +#define GFP_F2FS_HIGH_ZERO (GFP_NOFS | __GFP_ZERO | __GFP_HIGHMEM) + +/* + * For further optimization on multi-head logs, on-disk layout supports maximum + * 16 logs by default. The number, 16, is expected to cover all the cases + * enoughly. The implementaion currently uses no more than 6 logs. + * Half the logs are used for nodes, and the other half are used for data. + */ +#define MAX_ACTIVE_LOGS 16 +#define MAX_ACTIVE_NODE_LOGS 8 +#define MAX_ACTIVE_DATA_LOGS 8 + +/* + * For superblock + */ +struct f2fs_super_block { + __le32 magic; /* Magic Number */ + __le16 major_ver; /* Major Version */ + __le16 minor_ver; /* Minor Version */ + __le32 log_sectorsize; /* log2 sector size in bytes */ + __le32 log_sectors_per_block; /* log2 # of sectors per block */ + __le32 log_blocksize; /* log2 block size in bytes */ + __le32 log_blocks_per_seg; /* log2 # of blocks per segment */ + __le32 segs_per_sec; /* # of segments per section */ + __le32 secs_per_zone; /* # of sections per zone */ + __le32 checksum_offset; /* checksum offset inside super block */ + __le64 block_count; /* total # of user blocks */ + __le32 section_count; /* total # of sections */ + __le32 segment_count; /* total # of segments */ + __le32 segment_count_ckpt; /* # of segments for checkpoint */ + __le32 segment_count_sit; /* # of segments for SIT */ + __le32 segment_count_nat; /* # of segments for NAT */ + __le32 segment_count_ssa; /* # of segments for SSA */ + __le32 segment_count_main; /* # of segments for main area */ + __le32 segment0_blkaddr; /* start block address of segment 0 */ + __le32 cp_blkaddr; /* start block address of checkpoint */ + __le32 sit_blkaddr; /* start block address of SIT */ + __le32 nat_blkaddr; /* start block address of NAT */ + __le32 ssa_blkaddr; /* start block address of SSA */ + __le32 main_blkaddr; /* start block address of main area */ + __le32 root_ino; /* root inode number */ + __le32 node_ino; /* node inode number */ + __le32 meta_ino; /* meta inode number */ + __u8 uuid[16]; /* 128-bit uuid for volume */ + __le16 volume_name[512]; /* volume name */ + __le32 extension_count; /* # of extensions below */ + __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */ + __le32 cp_payload; +} __packed; + +/* + * For checkpoint + */ +#define CP_FASTBOOT_FLAG 0x00000020 +#define CP_FSCK_FLAG 0x00000010 +#define CP_ERROR_FLAG 0x00000008 +#define CP_COMPACT_SUM_FLAG 0x00000004 +#define CP_ORPHAN_PRESENT_FLAG 0x00000002 +#define CP_UMOUNT_FLAG 0x00000001 + +#define F2FS_CP_PACKS 2 /* # of checkpoint packs */ + +struct f2fs_checkpoint { + __le64 checkpoint_ver; /* checkpoint block version number */ + __le64 user_block_count; /* # of user blocks */ + __le64 valid_block_count; /* # of valid blocks in main area */ + __le32 rsvd_segment_count; /* # of reserved segments for gc */ + __le32 overprov_segment_count; /* # of overprovision segments */ + __le32 free_segment_count; /* # of free segments in main area */ + + /* information of current node segments */ + __le32 cur_node_segno[MAX_ACTIVE_NODE_LOGS]; + __le16 cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]; + /* information of current data segments */ + __le32 cur_data_segno[MAX_ACTIVE_DATA_LOGS]; + __le16 cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]; + __le32 ckpt_flags; /* Flags : umount and journal_present */ + __le32 cp_pack_total_block_count; /* total # of one cp pack */ + __le32 cp_pack_start_sum; /* start block number of data summary */ + __le32 valid_node_count; /* Total number of valid nodes */ + __le32 valid_inode_count; /* Total number of valid inodes */ + __le32 next_free_nid; /* Next free node number */ + __le32 sit_ver_bitmap_bytesize; /* Default value 64 */ + __le32 nat_ver_bitmap_bytesize; /* Default value 256 */ + __le32 checksum_offset; /* checksum offset inside cp block */ + __le64 elapsed_time; /* mounted time */ + /* allocation type of current segment */ + unsigned char alloc_type[MAX_ACTIVE_LOGS]; + + /* SIT and NAT version bitmap */ + unsigned char sit_nat_version_bitmap[1]; +} __packed; + +/* + * For orphan inode management + */ +#define F2FS_ORPHANS_PER_BLOCK 1020 + +#define GET_ORPHAN_BLOCKS(n) ((n + F2FS_ORPHANS_PER_BLOCK - 1) / \ + F2FS_ORPHANS_PER_BLOCK) + +struct f2fs_orphan_block { + __le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */ + __le32 reserved; /* reserved */ + __le16 blk_addr; /* block index in current CP */ + __le16 blk_count; /* Number of orphan inode blocks in CP */ + __le32 entry_count; /* Total number of orphan nodes in current CP */ + __le32 check_sum; /* CRC32 for orphan inode block */ +} __packed; + +/* + * For NODE structure + */ +struct f2fs_extent { + __le32 fofs; /* start file offset of the extent */ + __le32 blk; /* start block address of the extent */ + __le32 len; /* lengh of the extent */ +} __packed; + +#define F2FS_NAME_LEN 255 +#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ +#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ +#define DEF_NIDS_PER_INODE 5 /* Node IDs in an Inode */ +#define ADDRS_PER_INODE(fi) addrs_per_inode(fi) +#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ +#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ + +#define ADDRS_PER_PAGE(page, fi) \ + (IS_INODE(page) ? ADDRS_PER_INODE(fi) : ADDRS_PER_BLOCK) + +#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) +#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) +#define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3) +#define NODE_IND2_BLOCK (DEF_ADDRS_PER_INODE + 4) +#define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5) + +#define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ +#define F2FS_INLINE_DATA 0x02 /* file inline data flag */ +#define F2FS_INLINE_DENTRY 0x04 /* file inline dentry flag */ +#define F2FS_DATA_EXIST 0x08 /* file inline data exist flag */ +#define F2FS_INLINE_DOTS 0x10 /* file having implicit dot dentries */ + +#define MAX_INLINE_DATA (sizeof(__le32) * (DEF_ADDRS_PER_INODE - \ + F2FS_INLINE_XATTR_ADDRS - 1)) + +struct f2fs_inode { + __le16 i_mode; /* file mode */ + __u8 i_advise; /* file hints */ + __u8 i_inline; /* file inline flags */ + __le32 i_uid; /* user ID */ + __le32 i_gid; /* group ID */ + __le32 i_links; /* links count */ + __le64 i_size; /* file size in bytes */ + __le64 i_blocks; /* file size in blocks */ + __le64 i_atime; /* access time */ + __le64 i_ctime; /* change time */ + __le64 i_mtime; /* modification time */ + __le32 i_atime_nsec; /* access time in nano scale */ + __le32 i_ctime_nsec; /* change time in nano scale */ + __le32 i_mtime_nsec; /* modification time in nano scale */ + __le32 i_generation; /* file version (for NFS) */ + __le32 i_current_depth; /* only for directory depth */ + __le32 i_xattr_nid; /* nid to save xattr */ + __le32 i_flags; /* file attributes */ + __le32 i_pino; /* parent inode number */ + __le32 i_namelen; /* file name length */ + __u8 i_name[F2FS_NAME_LEN]; /* file name for SPOR */ + __u8 i_dir_level; /* dentry_level for large dir */ + + struct f2fs_extent i_ext; /* caching a largest extent */ + + __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ + + __le32 i_nid[DEF_NIDS_PER_INODE]; /* direct(2), indirect(2), + double_indirect(1) node id */ +} __packed; + +struct direct_node { + __le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */ +} __packed; + +struct indirect_node { + __le32 nid[NIDS_PER_BLOCK]; /* array of data block address */ +} __packed; + +enum { + COLD_BIT_SHIFT = 0, + FSYNC_BIT_SHIFT, + DENT_BIT_SHIFT, + OFFSET_BIT_SHIFT +}; + +#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */ + +struct node_footer { + __le32 nid; /* node id */ + __le32 ino; /* inode nunmber */ + __le32 flag; /* include cold/fsync/dentry marks and offset */ + __le64 cp_ver; /* checkpoint version */ + __le32 next_blkaddr; /* next node page block address */ +} __packed; + +struct f2fs_node { + /* can be one of three types: inode, direct, and indirect types */ + union { + struct f2fs_inode i; + struct direct_node dn; + struct indirect_node in; + }; + struct node_footer footer; +} __packed; + +/* + * For NAT entries + */ +#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry)) + +struct f2fs_nat_entry { + __u8 version; /* latest version of cached nat entry */ + __le32 ino; /* inode number */ + __le32 block_addr; /* block address */ +} __packed; + +struct f2fs_nat_block { + struct f2fs_nat_entry entries[NAT_ENTRY_PER_BLOCK]; +} __packed; + +/* + * For SIT entries + * + * Each segment is 2MB in size by default so that a bitmap for validity of + * there-in blocks should occupy 64 bytes, 512 bits. + * Not allow to change this. + */ +#define SIT_VBLOCK_MAP_SIZE 64 +#define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry)) + +/* + * Note that f2fs_sit_entry->vblocks has the following bit-field information. + * [15:10] : allocation type such as CURSEG_XXXX_TYPE + * [9:0] : valid block count + */ +#define SIT_VBLOCKS_SHIFT 10 +#define SIT_VBLOCKS_MASK ((1 << SIT_VBLOCKS_SHIFT) - 1) +#define GET_SIT_VBLOCKS(raw_sit) \ + (le16_to_cpu((raw_sit)->vblocks) & SIT_VBLOCKS_MASK) +#define GET_SIT_TYPE(raw_sit) \ + ((le16_to_cpu((raw_sit)->vblocks) & ~SIT_VBLOCKS_MASK) \ + >> SIT_VBLOCKS_SHIFT) + +struct f2fs_sit_entry { + __le16 vblocks; /* reference above */ + __u8 valid_map[SIT_VBLOCK_MAP_SIZE]; /* bitmap for valid blocks */ + __le64 mtime; /* segment age for cleaning */ +} __packed; + +struct f2fs_sit_block { + struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK]; +} __packed; + +/* + * For segment summary + * + * One summary block contains exactly 512 summary entries, which represents + * exactly 2MB segment by default. Not allow to change the basic units. + * + * NOTE: For initializing fields, you must use set_summary + * + * - If data page, nid represents dnode's nid + * - If node page, nid represents the node page's nid. + * + * The ofs_in_node is used by only data page. It represents offset + * from node's page's beginning to get a data block address. + * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node) + */ +#define ENTRIES_IN_SUM 512 +#define SUMMARY_SIZE (7) /* sizeof(struct summary) */ +#define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */ +#define SUM_ENTRY_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) + +/* a summary entry for a 4KB-sized block in a segment */ +struct f2fs_summary { + __le32 nid; /* parent node id */ + union { + __u8 reserved[3]; + struct { + __u8 version; /* node version number */ + __le16 ofs_in_node; /* block index in parent node */ + } __packed; + }; +} __packed; + +/* summary block type, node or data, is stored to the summary_footer */ +#define SUM_TYPE_NODE (1) +#define SUM_TYPE_DATA (0) + +struct summary_footer { + unsigned char entry_type; /* SUM_TYPE_XXX */ + __u32 check_sum; /* summary checksum */ +} __packed; + +#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\ + SUM_ENTRY_SIZE) +#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ + sizeof(struct nat_journal_entry)) +#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ + sizeof(struct nat_journal_entry)) +#define SIT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ + sizeof(struct sit_journal_entry)) +#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ + sizeof(struct sit_journal_entry)) +/* + * frequently updated NAT/SIT entries can be stored in the spare area in + * summary blocks + */ +enum { + NAT_JOURNAL = 0, + SIT_JOURNAL +}; + +struct nat_journal_entry { + __le32 nid; + struct f2fs_nat_entry ne; +} __packed; + +struct nat_journal { + struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; + __u8 reserved[NAT_JOURNAL_RESERVED]; +} __packed; + +struct sit_journal_entry { + __le32 segno; + struct f2fs_sit_entry se; +} __packed; + +struct sit_journal { + struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; + __u8 reserved[SIT_JOURNAL_RESERVED]; +} __packed; + +/* 4KB-sized summary block structure */ +struct f2fs_summary_block { + struct f2fs_summary entries[ENTRIES_IN_SUM]; + union { + __le16 n_nats; + __le16 n_sits; + }; + /* spare area is used by NAT or SIT journals */ + union { + struct nat_journal nat_j; + struct sit_journal sit_j; + }; + struct summary_footer footer; +} __packed; + +/* + * For directory operations + */ +#define F2FS_DOT_HASH 0 +#define F2FS_DDOT_HASH F2FS_DOT_HASH +#define F2FS_MAX_HASH (~((0x3ULL) << 62)) +#define F2FS_HASH_COL_BIT ((0x1ULL) << 63) + +typedef __le32 f2fs_hash_t; + +/* One directory entry slot covers 8bytes-long file name */ +#define F2FS_SLOT_LEN 8 +#define F2FS_SLOT_LEN_BITS 3 + +#define GET_DENTRY_SLOTS(x) ((x + F2FS_SLOT_LEN - 1) >> F2FS_SLOT_LEN_BITS) + +/* the number of dentry in a block */ +#define NR_DENTRY_IN_BLOCK 214 + +/* MAX level for dir lookup */ +#define MAX_DIR_HASH_DEPTH 63 + +/* MAX buckets in one level of dir */ +#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1)) + +#define SIZE_OF_DIR_ENTRY 11 /* by byte */ +#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ + BITS_PER_BYTE) +#define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \ + F2FS_SLOT_LEN) * \ + NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) + +/* One directory entry slot representing F2FS_SLOT_LEN-sized file name */ +struct f2fs_dir_entry { + __le32 hash_code; /* hash code of file name */ + __le32 ino; /* inode number */ + __le16 name_len; /* lengh of file name */ + __u8 file_type; /* file type */ +} __packed; + +/* 4KB-sized directory entry block */ +struct f2fs_dentry_block { + /* validity bitmap for directory entries in each block */ + __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; + __u8 reserved[SIZE_OF_RESERVED]; + struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; + __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; +} __packed; + +/* for inline dir */ +#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + \ + BITS_PER_BYTE - 1) / BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY + INLINE_DENTRY_BITMAP_SIZE)) + +/* inline directory entry structure */ +struct f2fs_inline_dentry { + __u8 dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; + __u8 reserved[INLINE_RESERVED_SIZE]; + struct f2fs_dir_entry dentry[NR_INLINE_DENTRY]; + __u8 filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; +} __packed; + +/* file types used in inode_info->flags */ +enum { + F2FS_FT_UNKNOWN, + F2FS_FT_REG_FILE, + F2FS_FT_DIR, + F2FS_FT_CHRDEV, + F2FS_FT_BLKDEV, + F2FS_FT_FIFO, + F2FS_FT_SOCK, + F2FS_FT_SYMLINK, + F2FS_FT_MAX +}; + +#endif /* _LINUX_F2FS_FS_H */ diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h new file mode 100644 index 000000000000..f622e782ffab --- /dev/null +++ b/include/trace/events/f2fs.h @@ -0,0 +1,1174 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM f2fs + +#if !defined(_TRACE_F2FS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_F2FS_H + +#include + +#define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev) +#define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino + +#define show_block_type(type) \ + __print_symbolic(type, \ + { NODE, "NODE" }, \ + { DATA, "DATA" }, \ + { META, "META" }, \ + { META_FLUSH, "META_FLUSH" }, \ + { INMEM, "INMEM" }, \ + { INMEM_DROP, "INMEM_DROP" }, \ + { IPU, "IN-PLACE" }, \ + { OPU, "OUT-OF-PLACE" }) + +#define F2FS_BIO_MASK(t) (t & (READA | WRITE_FLUSH_FUA)) +#define F2FS_BIO_EXTRA_MASK(t) (t & (REQ_META | REQ_PRIO)) + +#define show_bio_type(type) show_bio_base(type), show_bio_extra(type) + +#define show_bio_base(type) \ + __print_symbolic(F2FS_BIO_MASK(type), \ + { READ, "READ" }, \ + { READA, "READAHEAD" }, \ + { READ_SYNC, "READ_SYNC" }, \ + { WRITE, "WRITE" }, \ + { WRITE_SYNC, "WRITE_SYNC" }, \ + { WRITE_FLUSH, "WRITE_FLUSH" }, \ + { WRITE_FUA, "WRITE_FUA" }, \ + { WRITE_FLUSH_FUA, "WRITE_FLUSH_FUA" }) + +#define show_bio_extra(type) \ + __print_symbolic(F2FS_BIO_EXTRA_MASK(type), \ + { REQ_META, "(M)" }, \ + { REQ_PRIO, "(P)" }, \ + { REQ_META | REQ_PRIO, "(MP)" }, \ + { 0, " \b" }) + +#define show_data_type(type) \ + __print_symbolic(type, \ + { CURSEG_HOT_DATA, "Hot DATA" }, \ + { CURSEG_WARM_DATA, "Warm DATA" }, \ + { CURSEG_COLD_DATA, "Cold DATA" }, \ + { CURSEG_HOT_NODE, "Hot NODE" }, \ + { CURSEG_WARM_NODE, "Warm NODE" }, \ + { CURSEG_COLD_NODE, "Cold NODE" }, \ + { NO_CHECK_TYPE, "No TYPE" }) + +#define show_file_type(type) \ + __print_symbolic(type, \ + { 0, "FILE" }, \ + { 1, "DIR" }) + +#define show_gc_type(type) \ + __print_symbolic(type, \ + { FG_GC, "Foreground GC" }, \ + { BG_GC, "Background GC" }) + +#define show_alloc_mode(type) \ + __print_symbolic(type, \ + { LFS, "LFS-mode" }, \ + { SSR, "SSR-mode" }) + +#define show_victim_policy(type) \ + __print_symbolic(type, \ + { GC_GREEDY, "Greedy" }, \ + { GC_CB, "Cost-Benefit" }) + +#define show_cpreason(type) \ + __print_symbolic(type, \ + { CP_UMOUNT, "Umount" }, \ + { CP_FASTBOOT, "Fastboot" }, \ + { CP_SYNC, "Sync" }, \ + { CP_RECOVERY, "Recovery" }, \ + { CP_DISCARD, "Discard" }) + +struct victim_sel_policy; + +DECLARE_EVENT_CLASS(f2fs__inode, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(ino_t, pino) + __field(umode_t, mode) + __field(loff_t, size) + __field(unsigned int, nlink) + __field(blkcnt_t, blocks) + __field(__u8, advise) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pino = F2FS_I(inode)->i_pino; + __entry->mode = inode->i_mode; + __entry->nlink = inode->i_nlink; + __entry->size = inode->i_size; + __entry->blocks = inode->i_blocks; + __entry->advise = F2FS_I(inode)->i_advise; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pino = %lu, i_mode = 0x%hx, " + "i_size = %lld, i_nlink = %u, i_blocks = %llu, i_advise = 0x%x", + show_dev_ino(__entry), + (unsigned long)__entry->pino, + __entry->mode, + __entry->size, + (unsigned int)__entry->nlink, + (unsigned long long)__entry->blocks, + (unsigned char)__entry->advise) +); + +DECLARE_EVENT_CLASS(f2fs__inode_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, ret = %d", + show_dev_ino(__entry), + __entry->ret) +); + +DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +TRACE_EVENT(f2fs_sync_file_exit, + + TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret), + + TP_ARGS(inode, need_cp, datasync, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, need_cp) + __field(int, datasync) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->need_cp = need_cp; + __entry->datasync = datasync; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, " + "datasync = %d, ret = %d", + show_dev_ino(__entry), + __entry->need_cp ? "needed" : "not needed", + __entry->datasync, + __entry->ret) +); + +TRACE_EVENT(f2fs_sync_fs, + + TP_PROTO(struct super_block *sb, int wait), + + TP_ARGS(sb, wait), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, dirty) + __field(int, wait) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->dirty = is_sbi_flag_set(F2FS_SB(sb), SBI_IS_DIRTY); + __entry->wait = wait; + ), + + TP_printk("dev = (%d,%d), superblock is %s, wait = %d", + show_dev(__entry), + __entry->dirty ? "dirty" : "not dirty", + __entry->wait) +); + +DEFINE_EVENT(f2fs__inode, f2fs_iget, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_iget_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +DEFINE_EVENT(f2fs__inode, f2fs_evict_inode, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_new_inode, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +TRACE_EVENT(f2fs_unlink_enter, + + TP_PROTO(struct inode *dir, struct dentry *dentry), + + TP_ARGS(dir, dentry), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, size) + __field(blkcnt_t, blocks) + __field(const char *, name) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->size = dir->i_size; + __entry->blocks = dir->i_blocks; + __entry->name = dentry->d_name.name; + ), + + TP_printk("dev = (%d,%d), dir ino = %lu, i_size = %lld, " + "i_blocks = %llu, name = %s", + show_dev_ino(__entry), + __entry->size, + (unsigned long long)__entry->blocks, + __entry->name) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_unlink_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +DEFINE_EVENT(f2fs__inode, f2fs_truncate, + + TP_PROTO(struct inode *inode), + + TP_ARGS(inode) +); + +TRACE_EVENT(f2fs_truncate_data_blocks_range, + + TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs, int free), + + TP_ARGS(inode, nid, ofs, free), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(nid_t, nid) + __field(unsigned int, ofs) + __field(int, free) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->nid = nid; + __entry->ofs = ofs; + __entry->free = free; + ), + + TP_printk("dev = (%d,%d), ino = %lu, nid = %u, offset = %u, freed = %d", + show_dev_ino(__entry), + (unsigned int)__entry->nid, + __entry->ofs, + __entry->free) +); + +DECLARE_EVENT_CLASS(f2fs__truncate_op, + + TP_PROTO(struct inode *inode, u64 from), + + TP_ARGS(inode, from), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, size) + __field(blkcnt_t, blocks) + __field(u64, from) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->size = inode->i_size; + __entry->blocks = inode->i_blocks; + __entry->from = from; + ), + + TP_printk("dev = (%d,%d), ino = %lu, i_size = %lld, i_blocks = %llu, " + "start file offset = %llu", + show_dev_ino(__entry), + __entry->size, + (unsigned long long)__entry->blocks, + (unsigned long long)__entry->from) +); + +DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_blocks_enter, + + TP_PROTO(struct inode *inode, u64 from), + + TP_ARGS(inode, from) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_blocks_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +DEFINE_EVENT(f2fs__truncate_op, f2fs_truncate_inode_blocks_enter, + + TP_PROTO(struct inode *inode, u64 from), + + TP_ARGS(inode, from) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_inode_blocks_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +DECLARE_EVENT_CLASS(f2fs__truncate_node, + + TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr), + + TP_ARGS(inode, nid, blk_addr), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(nid_t, nid) + __field(block_t, blk_addr) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->nid = nid; + __entry->blk_addr = blk_addr; + ), + + TP_printk("dev = (%d,%d), ino = %lu, nid = %u, block_address = 0x%llx", + show_dev_ino(__entry), + (unsigned int)__entry->nid, + (unsigned long long)__entry->blk_addr) +); + +DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_nodes_enter, + + TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr), + + TP_ARGS(inode, nid, blk_addr) +); + +DEFINE_EVENT(f2fs__inode_exit, f2fs_truncate_nodes_exit, + + TP_PROTO(struct inode *inode, int ret), + + TP_ARGS(inode, ret) +); + +DEFINE_EVENT(f2fs__truncate_node, f2fs_truncate_node, + + TP_PROTO(struct inode *inode, nid_t nid, block_t blk_addr), + + TP_ARGS(inode, nid, blk_addr) +); + +TRACE_EVENT(f2fs_truncate_partial_nodes, + + TP_PROTO(struct inode *inode, nid_t nid[], int depth, int err), + + TP_ARGS(inode, nid, depth, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(nid_t, nid[3]) + __field(int, depth) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->nid[0] = nid[0]; + __entry->nid[1] = nid[1]; + __entry->nid[2] = nid[2]; + __entry->depth = depth; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), ino = %lu, " + "nid[0] = %u, nid[1] = %u, nid[2] = %u, depth = %d, err = %d", + show_dev_ino(__entry), + (unsigned int)__entry->nid[0], + (unsigned int)__entry->nid[1], + (unsigned int)__entry->nid[2], + __entry->depth, + __entry->err) +); + +TRACE_EVENT(f2fs_get_data_block, + TP_PROTO(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int ret), + + TP_ARGS(inode, iblock, bh, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(sector_t, iblock) + __field(sector_t, bh_start) + __field(size_t, bh_size) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->iblock = iblock; + __entry->bh_start = bh->b_blocknr; + __entry->bh_size = bh->b_size; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, file offset = %llu, " + "start blkaddr = 0x%llx, len = 0x%llx bytes, err = %d", + show_dev_ino(__entry), + (unsigned long long)__entry->iblock, + (unsigned long long)__entry->bh_start, + (unsigned long long)__entry->bh_size, + __entry->ret) +); + +TRACE_EVENT(f2fs_get_victim, + + TP_PROTO(struct super_block *sb, int type, int gc_type, + struct victim_sel_policy *p, unsigned int pre_victim, + unsigned int prefree, unsigned int free), + + TP_ARGS(sb, type, gc_type, p, pre_victim, prefree, free), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, type) + __field(int, gc_type) + __field(int, alloc_mode) + __field(int, gc_mode) + __field(unsigned int, victim) + __field(unsigned int, ofs_unit) + __field(unsigned int, pre_victim) + __field(unsigned int, prefree) + __field(unsigned int, free) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->type = type; + __entry->gc_type = gc_type; + __entry->alloc_mode = p->alloc_mode; + __entry->gc_mode = p->gc_mode; + __entry->victim = p->min_segno; + __entry->ofs_unit = p->ofs_unit; + __entry->pre_victim = pre_victim; + __entry->prefree = prefree; + __entry->free = free; + ), + + TP_printk("dev = (%d,%d), type = %s, policy = (%s, %s, %s), victim = %u " + "ofs_unit = %u, pre_victim_secno = %d, prefree = %u, free = %u", + show_dev(__entry), + show_data_type(__entry->type), + show_gc_type(__entry->gc_type), + show_alloc_mode(__entry->alloc_mode), + show_victim_policy(__entry->gc_mode), + __entry->victim, + __entry->ofs_unit, + (int)__entry->pre_victim, + __entry->prefree, + __entry->free) +); + +TRACE_EVENT(f2fs_fallocate, + + TP_PROTO(struct inode *inode, int mode, + loff_t offset, loff_t len, int ret), + + TP_ARGS(inode, mode, offset, len, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, mode) + __field(loff_t, offset) + __field(loff_t, len) + __field(loff_t, size) + __field(blkcnt_t, blocks) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->mode = mode; + __entry->offset = offset; + __entry->len = len; + __entry->size = inode->i_size; + __entry->blocks = inode->i_blocks; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu, mode = %x, offset = %lld, " + "len = %lld, i_size = %lld, i_blocks = %llu, ret = %d", + show_dev_ino(__entry), + __entry->mode, + (unsigned long long)__entry->offset, + (unsigned long long)__entry->len, + (unsigned long long)__entry->size, + (unsigned long long)__entry->blocks, + __entry->ret) +); + +TRACE_EVENT(f2fs_direct_IO_enter, + + TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, int rw), + + TP_ARGS(inode, offset, len, rw), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned long, len) + __field(int, rw) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = offset; + __entry->len = len; + __entry->rw = rw; + ), + + TP_printk("dev = (%d,%d), ino = %lu pos = %lld len = %lu rw = %d", + show_dev_ino(__entry), + __entry->pos, + __entry->len, + __entry->rw) +); + +TRACE_EVENT(f2fs_direct_IO_exit, + + TP_PROTO(struct inode *inode, loff_t offset, unsigned long len, + int rw, int ret), + + TP_ARGS(inode, offset, len, rw, ret), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned long, len) + __field(int, rw) + __field(int, ret) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = offset; + __entry->len = len; + __entry->rw = rw; + __entry->ret = ret; + ), + + TP_printk("dev = (%d,%d), ino = %lu pos = %lld len = %lu " + "rw = %d ret = %d", + show_dev_ino(__entry), + __entry->pos, + __entry->len, + __entry->rw, + __entry->ret) +); + +TRACE_EVENT(f2fs_reserve_new_block, + + TP_PROTO(struct inode *inode, nid_t nid, unsigned int ofs_in_node), + + TP_ARGS(inode, nid, ofs_in_node), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(nid_t, nid) + __field(unsigned int, ofs_in_node) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->nid = nid; + __entry->ofs_in_node = ofs_in_node; + ), + + TP_printk("dev = (%d,%d), nid = %u, ofs_in_node = %u", + show_dev(__entry), + (unsigned int)__entry->nid, + __entry->ofs_in_node) +); + +DECLARE_EVENT_CLASS(f2fs__submit_page_bio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(pgoff_t, index) + __field(block_t, blkaddr) + __field(int, rw) + __field(int, type) + ), + + TP_fast_assign( + __entry->dev = page->mapping->host->i_sb->s_dev; + __entry->ino = page->mapping->host->i_ino; + __entry->index = page->index; + __entry->blkaddr = fio->blk_addr; + __entry->rw = fio->rw; + __entry->type = fio->type; + ), + + TP_printk("dev = (%d,%d), ino = %lu, page_index = 0x%lx, " + "blkaddr = 0x%llx, rw = %s%s, type = %s", + show_dev_ino(__entry), + (unsigned long)__entry->index, + (unsigned long long)__entry->blkaddr, + show_bio_type(__entry->rw), + show_block_type(__entry->type)) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_bio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_CONDITION(page->mapping) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_page_bio, f2fs_submit_page_mbio, + + TP_PROTO(struct page *page, struct f2fs_io_info *fio), + + TP_ARGS(page, fio), + + TP_CONDITION(page->mapping) +); + +DECLARE_EVENT_CLASS(f2fs__submit_bio, + + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), + + TP_ARGS(sb, fio, bio), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, rw) + __field(int, type) + __field(sector_t, sector) + __field(unsigned int, size) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->rw = fio->rw; + __entry->type = fio->type; + __entry->sector = bio->bi_sector; + __entry->size = bio->bi_size; + ), + + TP_printk("dev = (%d,%d), %s%s, %s, sector = %lld, size = %u", + show_dev(__entry), + show_bio_type(__entry->rw), + show_block_type(__entry->type), + (unsigned long long)__entry->sector, + __entry->size) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_write_bio, + + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), + + TP_ARGS(sb, fio, bio), + + TP_CONDITION(bio) +); + +DEFINE_EVENT_CONDITION(f2fs__submit_bio, f2fs_submit_read_bio, + + TP_PROTO(struct super_block *sb, struct f2fs_io_info *fio, + struct bio *bio), + + TP_ARGS(sb, fio, bio), + + TP_CONDITION(bio) +); + +TRACE_EVENT(f2fs_write_begin, + + TP_PROTO(struct inode *inode, loff_t pos, unsigned int len, + unsigned int flags), + + TP_ARGS(inode, pos, len, flags), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned int, len) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = pos; + __entry->len = len; + __entry->flags = flags; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pos = %llu, len = %u, flags = %u", + show_dev_ino(__entry), + (unsigned long long)__entry->pos, + __entry->len, + __entry->flags) +); + +TRACE_EVENT(f2fs_write_end, + + TP_PROTO(struct inode *inode, loff_t pos, unsigned int len, + unsigned int copied), + + TP_ARGS(inode, pos, len, copied), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, pos) + __field(unsigned int, len) + __field(unsigned int, copied) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pos = pos; + __entry->len = len; + __entry->copied = copied; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pos = %llu, len = %u, copied = %u", + show_dev_ino(__entry), + (unsigned long long)__entry->pos, + __entry->len, + __entry->copied) +); + +DECLARE_EVENT_CLASS(f2fs__page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, type) + __field(int, dir) + __field(pgoff_t, index) + __field(int, dirty) + __field(int, uptodate) + ), + + TP_fast_assign( + __entry->dev = page->mapping->host->i_sb->s_dev; + __entry->ino = page->mapping->host->i_ino; + __entry->type = type; + __entry->dir = S_ISDIR(page->mapping->host->i_mode); + __entry->index = page->index; + __entry->dirty = PageDirty(page); + __entry->uptodate = PageUptodate(page); + ), + + TP_printk("dev = (%d,%d), ino = %lu, %s, %s, index = %lu, " + "dirty = %d, uptodate = %d", + show_dev_ino(__entry), + show_block_type(__entry->type), + show_file_type(__entry->dir), + (unsigned long)__entry->index, + __entry->dirty, + __entry->uptodate) +); + +DEFINE_EVENT(f2fs__page, f2fs_writepage, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_do_write_data_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_readpage, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_set_page_dirty, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_vm_page_mkwrite, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_register_inmem_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +DEFINE_EVENT(f2fs__page, f2fs_commit_inmem_page, + + TP_PROTO(struct page *page, int type), + + TP_ARGS(page, type) +); + +TRACE_EVENT(f2fs_writepages, + + TP_PROTO(struct inode *inode, struct writeback_control *wbc, int type), + + TP_ARGS(inode, wbc, type), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(int, type) + __field(int, dir) + __field(long, nr_to_write) + __field(long, pages_skipped) + __field(loff_t, range_start) + __field(loff_t, range_end) + __field(pgoff_t, writeback_index) + __field(int, sync_mode) + __field(char, for_kupdate) + __field(char, for_background) + __field(char, tagged_writepages) + __field(char, for_reclaim) + __field(char, range_cyclic) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->type = type; + __entry->dir = S_ISDIR(inode->i_mode); + __entry->nr_to_write = wbc->nr_to_write; + __entry->pages_skipped = wbc->pages_skipped; + __entry->range_start = wbc->range_start; + __entry->range_end = wbc->range_end; + __entry->writeback_index = inode->i_mapping->writeback_index; + __entry->sync_mode = wbc->sync_mode; + __entry->for_kupdate = wbc->for_kupdate; + __entry->for_background = wbc->for_background; + __entry->tagged_writepages = wbc->tagged_writepages; + __entry->for_reclaim = wbc->for_reclaim; + __entry->range_cyclic = wbc->range_cyclic; + ), + + TP_printk("dev = (%d,%d), ino = %lu, %s, %s, nr_to_write %ld, " + "skipped %ld, start %lld, end %lld, wb_idx %lu, sync_mode %d, " + "kupdate %u background %u tagged %u reclaim %u cyclic %u", + show_dev_ino(__entry), + show_block_type(__entry->type), + show_file_type(__entry->dir), + __entry->nr_to_write, + __entry->pages_skipped, + __entry->range_start, + __entry->range_end, + (unsigned long)__entry->writeback_index, + __entry->sync_mode, + __entry->for_kupdate, + __entry->for_background, + __entry->tagged_writepages, + __entry->for_reclaim, + __entry->range_cyclic) +); + +TRACE_EVENT(f2fs_write_checkpoint, + + TP_PROTO(struct super_block *sb, int reason, char *msg), + + TP_ARGS(sb, reason, msg), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(int, reason) + __field(char *, msg) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->reason = reason; + __entry->msg = msg; + ), + + TP_printk("dev = (%d,%d), checkpoint for %s, state = %s", + show_dev(__entry), + show_cpreason(__entry->reason), + __entry->msg) +); + +TRACE_EVENT(f2fs_issue_discard, + + TP_PROTO(struct super_block *sb, block_t blkstart, block_t blklen), + + TP_ARGS(sb, blkstart, blklen), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(block_t, blkstart) + __field(block_t, blklen) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->blkstart = blkstart; + __entry->blklen = blklen; + ), + + TP_printk("dev = (%d,%d), blkstart = 0x%llx, blklen = 0x%llx", + show_dev(__entry), + (unsigned long long)__entry->blkstart, + (unsigned long long)__entry->blklen) +); + +TRACE_EVENT(f2fs_issue_flush, + + TP_PROTO(struct super_block *sb, unsigned int nobarrier, + unsigned int flush_merge), + + TP_ARGS(sb, nobarrier, flush_merge), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, nobarrier) + __field(unsigned int, flush_merge) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->nobarrier = nobarrier; + __entry->flush_merge = flush_merge; + ), + + TP_printk("dev = (%d,%d), %s %s", + show_dev(__entry), + __entry->nobarrier ? "skip (nobarrier)" : "issue", + __entry->flush_merge ? " with flush_merge" : "") +); + +TRACE_EVENT(f2fs_lookup_extent_tree_start, + + TP_PROTO(struct inode *inode, unsigned int pgofs), + + TP_ARGS(inode, pgofs), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u", + show_dev_ino(__entry), + __entry->pgofs) +); + +TRACE_EVENT_CONDITION(f2fs_lookup_extent_tree_end, + + TP_PROTO(struct inode *inode, unsigned int pgofs, + struct extent_node *en), + + TP_ARGS(inode, pgofs, en), + + TP_CONDITION(en), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(unsigned int, fofs) + __field(u32, blk) + __field(unsigned int, len) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->fofs = en->ei.fofs; + __entry->blk = en->ei.blk; + __entry->len = en->ei.len; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, " + "ext_info(fofs: %u, blk: %u, len: %u)", + show_dev_ino(__entry), + __entry->pgofs, + __entry->fofs, + __entry->blk, + __entry->len) +); + +TRACE_EVENT(f2fs_update_extent_tree, + + TP_PROTO(struct inode *inode, unsigned int pgofs, block_t blkaddr), + + TP_ARGS(inode, pgofs, blkaddr), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, pgofs) + __field(u32, blk) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->pgofs = pgofs; + __entry->blk = blkaddr; + ), + + TP_printk("dev = (%d,%d), ino = %lu, pgofs = %u, blkaddr = %u", + show_dev_ino(__entry), + __entry->pgofs, + __entry->blk) +); + +TRACE_EVENT(f2fs_shrink_extent_tree, + + TP_PROTO(struct f2fs_sb_info *sbi, unsigned int node_cnt, + unsigned int tree_cnt), + + TP_ARGS(sbi, node_cnt, tree_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned int, node_cnt) + __field(unsigned int, tree_cnt) + ), + + TP_fast_assign( + __entry->dev = sbi->sb->s_dev; + __entry->node_cnt = node_cnt; + __entry->tree_cnt = tree_cnt; + ), + + TP_printk("dev = (%d,%d), shrunk: node_cnt = %u, tree_cnt = %u", + show_dev(__entry), + __entry->node_cnt, + __entry->tree_cnt) +); + +TRACE_EVENT(f2fs_destroy_extent_tree, + + TP_PROTO(struct inode *inode, unsigned int node_cnt), + + TP_ARGS(inode, node_cnt), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(unsigned int, node_cnt) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->node_cnt = node_cnt; + ), + + TP_printk("dev = (%d,%d), ino = %lu, destroyed: node_cnt = %u", + show_dev_ino(__entry), + __entry->node_cnt) +); + +#endif /* _TRACE_F2FS_H */ + + /* This part must be outside protection */ +#include From 1acc9157be9b9636c0d06ce97f594630bebe7b20 Mon Sep 17 00:00:00 2001 From: vm03 Date: Fri, 21 Aug 2015 16:06:51 +0300 Subject: [PATCH 096/104] defconfig: add f2fs Change-Id: I63e44f8764706f3675d03836c2292ce078db2e6e --- arch/arm/configs/w3ds_global_com_defconfig | 6 ++++++ arch/arm/configs/w5n_global_com_defconfig | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index 1e6f105d1bc5..36bfcdadf962 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -502,6 +502,12 @@ CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_ECRYPT_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_CHECK_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 54328d1d4328..8efb702fd6ff 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -504,6 +504,12 @@ CONFIG_FUSE_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_ECRYPT_FS=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_CHECK_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y From 3a7d1be3bd9ee816c02ff67db70fa106aebbd84e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Sat, 15 Dec 2012 22:29:58 -0800 Subject: [PATCH 097/104] block: fiops ioscheduler core FIOPS (Fair IOPS) ioscheduler is IOPS based ioscheduler, so only targets for drive without I/O seek. It's quite similar like CFQ, but the dispatch decision is made according to IOPS instead of slice. The algorithm is simple. Drive has a service tree, and each task lives in the tree. The key into the tree is called vios (virtual I/O). Every request has vios, which is calculated according to its ioprio, request size and so on. Task's vios is the sum of vios of all requests it dispatches. FIOPS always selects task with minimum vios in the service tree and let the task dispatch request. The dispatched request's vios is then added to the task's vios and the task is repositioned in the sevice tree. Unlike CFQ, FIOPS doesn't have separate sync/async queues, because with I/O less writeback, usually a task can only dispatch either sync or async requests. Bias read or write request can still be done with read/write scale. One issue is if workload iodepth is lower than drive queue_depth, IOPS share of a task might not be strictly according to its priority, request size and so on. In this case, the drive is in idle actually. Solving the problem need make drive idle, so impact performance. I believe CFQ isn't completely fair between tasks in such case too. Signed-off-by: Shaohua Li block: fiops read/write request scale read/write speed of Flash based storage usually is different. For example, in my SSD maxium thoughput of read is about 3 times faster than that of write. Add a scale to differenate read and write. Also add a tunable, so user can assign different scale for read and write. By default, the scale is 1:1, which means the scale is a noop. Signed-off-by: Shaohua Li block: fiops sync/async scale CFQ gives 2.5 times more share to sync workload. This matches CFQ. Note this is different with the read/write scale. We have 3 types of requests: 1. read 2. sync write 3. write CFQ doesn't differentitate type 1 and 2, but request cost of 1 and 2 are usually different for flash based storage. So we have both sync/async and read/write scale here. Signed-off-by: Shaohua Li block: fiops add ioprio support Add CFQ-like ioprio support. Priority A will get 20% more share than priority A+1, which matches CFQ. Signed-off-by: Shaohua Li block: fiops preserve vios key for deep queue depth workload If the task has running request, even it's added into service tree newly, we preserve its vios key, so it will not lost its share. This should work for task driving big queue depth. For single depth task, there is no approach to preserve its vios key. Signed-off-by: Shaohua Li block: fiops bias sync workload If there are async requests running, delay async workload. Otherwise async workload (usually very deep iodepth) will use all queue iodepth and later sync requests will get long delayed. The idea is from CFQ. Signed-off-by: Shaohua Li block: fiops add some trace information Add some trace information, which is helpful when I do debugging. Change-Id: I971fcef95e7fdb6360b0e07cffefc0b51a6fbbc0 Signed-off-by: Shaohua Li --- block/Kconfig.iosched | 12 + block/Makefile | 1 + block/fiops-iosched.c | 753 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 766 insertions(+) create mode 100644 block/fiops-iosched.c diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 5fd98eaed10d..6be069a5f6a1 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -65,6 +65,14 @@ config CFQ_GROUP_IOSCHED ---help--- Enable group IO scheduling in CFQ. +config IOSCHED_FIOPS + tristate "IOPS based I/O scheduler" + default y + ---help--- + This is an IOPS based I/O scheduler. It will try to distribute + IOPS equally among all processes in the system. It's mainly for + Flash based storage. + choice prompt "Default I/O scheduler" default DEFAULT_CFQ @@ -88,6 +96,9 @@ choice config DEFAULT_CFQ bool "CFQ" if IOSCHED_CFQ=y + config DEFAULT_FIOPS + bool "FIOPS" if IOSCHED_FIOPS=y + config DEFAULT_NOOP bool "No-op" @@ -98,6 +109,7 @@ config DEFAULT_IOSCHED default "deadline" if DEFAULT_DEADLINE default "row" if DEFAULT_ROW default "cfq" if DEFAULT_CFQ + default "fiops" if DEFAULT_FIOPS default "noop" if DEFAULT_NOOP endmenu diff --git a/block/Makefile b/block/Makefile index b5e663709b3f..afc813ae165e 100644 --- a/block/Makefile +++ b/block/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_ROW) += row-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o obj-$(CONFIG_IOSCHED_TEST) += test-iosched.o +obj-$(CONFIG_IOSCHED_FIOPS) += fiops-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/block/fiops-iosched.c b/block/fiops-iosched.c new file mode 100644 index 000000000000..671b1d33157d --- /dev/null +++ b/block/fiops-iosched.c @@ -0,0 +1,753 @@ +/* + * IOPS based IO scheduler. Based on CFQ. + * Copyright (C) 2003 Jens Axboe + * Shaohua Li + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "blk.h" + +#define VIOS_SCALE_SHIFT 10 +#define VIOS_SCALE (1 << VIOS_SCALE_SHIFT) + +#define VIOS_READ_SCALE (1) +#define VIOS_WRITE_SCALE (1) +#define VIOS_SYNC_SCALE (2) +#define VIOS_ASYNC_SCALE (5) + +#define VIOS_PRIO_SCALE (5) + +struct fiops_rb_root { + struct rb_root rb; + struct rb_node *left; + unsigned count; + + u64 min_vios; +}; +#define FIOPS_RB_ROOT (struct fiops_rb_root) { .rb = RB_ROOT} + +enum wl_prio_t { + IDLE_WORKLOAD = 0, + BE_WORKLOAD = 1, + RT_WORKLOAD = 2, + FIOPS_PRIO_NR, +}; + +struct fiops_data { + struct request_queue *queue; + + struct fiops_rb_root service_tree[FIOPS_PRIO_NR]; + + unsigned int busy_queues; + unsigned int in_flight[2]; + + struct work_struct unplug_work; + + unsigned int read_scale; + unsigned int write_scale; + unsigned int sync_scale; + unsigned int async_scale; +}; + +struct fiops_ioc { + struct io_cq icq; + + unsigned int flags; + struct fiops_data *fiopsd; + struct rb_node rb_node; + u64 vios; /* key in service_tree */ + struct fiops_rb_root *service_tree; + + unsigned int in_flight; + + struct rb_root sort_list; + struct list_head fifo; + + pid_t pid; + unsigned short ioprio; + enum wl_prio_t wl_type; +}; + +#define ioc_service_tree(ioc) (&((ioc)->fiopsd->service_tree[(ioc)->wl_type])) +#define RQ_CIC(rq) icq_to_cic((rq)->elv.icq) + +enum ioc_state_flags { + FIOPS_IOC_FLAG_on_rr = 0, /* on round-robin busy list */ + FIOPS_IOC_FLAG_prio_changed, /* task priority has changed */ +}; + +#define FIOPS_IOC_FNS(name) \ +static inline void fiops_mark_ioc_##name(struct fiops_ioc *ioc) \ +{ \ + ioc->flags |= (1 << FIOPS_IOC_FLAG_##name); \ +} \ +static inline void fiops_clear_ioc_##name(struct fiops_ioc *ioc) \ +{ \ + ioc->flags &= ~(1 << FIOPS_IOC_FLAG_##name); \ +} \ +static inline int fiops_ioc_##name(const struct fiops_ioc *ioc) \ +{ \ + return ((ioc)->flags & (1 << FIOPS_IOC_FLAG_##name)) != 0; \ +} + +FIOPS_IOC_FNS(on_rr); +FIOPS_IOC_FNS(prio_changed); +#undef FIOPS_IOC_FNS + +#define fiops_log_ioc(fiopsd, ioc, fmt, args...) \ + blk_add_trace_msg((fiopsd)->queue, "ioc%d " fmt, (ioc)->pid, ##args) +#define fiops_log(fiopsd, fmt, args...) \ + blk_add_trace_msg((fiopsd)->queue, "fiops " fmt, ##args) + +enum wl_prio_t fiops_wl_type(short prio_class) +{ + if (prio_class == IOPRIO_CLASS_RT) + return RT_WORKLOAD; + if (prio_class == IOPRIO_CLASS_BE) + return BE_WORKLOAD; + return IDLE_WORKLOAD; +} + +static inline struct fiops_ioc *icq_to_cic(struct io_cq *icq) +{ + /* cic->icq is the first member, %NULL will convert to %NULL */ + return container_of(icq, struct fiops_ioc, icq); +} + +static inline struct fiops_ioc *fiops_cic_lookup(struct fiops_data *fiopsd, + struct io_context *ioc) +{ + if (ioc) + return icq_to_cic(ioc_lookup_icq(ioc, fiopsd->queue)); + return NULL; +} + +/* + * The below is leftmost cache rbtree addon + */ +static struct fiops_ioc *fiops_rb_first(struct fiops_rb_root *root) +{ + /* Service tree is empty */ + if (!root->count) + return NULL; + + if (!root->left) + root->left = rb_first(&root->rb); + + if (root->left) + return rb_entry(root->left, struct fiops_ioc, rb_node); + + return NULL; +} + +static void rb_erase_init(struct rb_node *n, struct rb_root *root) +{ + rb_erase(n, root); + RB_CLEAR_NODE(n); +} + +static void fiops_rb_erase(struct rb_node *n, struct fiops_rb_root *root) +{ + if (root->left == n) + root->left = NULL; + rb_erase_init(n, &root->rb); + --root->count; +} + +static inline u64 max_vios(u64 min_vios, u64 vios) +{ + s64 delta = (s64)(vios - min_vios); + if (delta > 0) + min_vios = vios; + + return min_vios; +} + +static void fiops_update_min_vios(struct fiops_rb_root *service_tree) +{ + struct fiops_ioc *ioc; + + ioc = fiops_rb_first(service_tree); + if (!ioc) + return; + service_tree->min_vios = max_vios(service_tree->min_vios, ioc->vios); +} + +/* + * The fiopsd->service_trees holds all pending fiops_ioc's that have + * requests waiting to be processed. It is sorted in the order that + * we will service the queues. + */ +static void fiops_service_tree_add(struct fiops_data *fiopsd, + struct fiops_ioc *ioc) +{ + struct rb_node **p, *parent; + struct fiops_ioc *__ioc; + struct fiops_rb_root *service_tree = ioc_service_tree(ioc); + u64 vios; + int left; + + /* New added IOC */ + if (RB_EMPTY_NODE(&ioc->rb_node)) { + if (ioc->in_flight > 0) + vios = ioc->vios; + else + vios = max_vios(service_tree->min_vios, ioc->vios); + } else { + vios = ioc->vios; + /* ioc->service_tree might not equal to service_tree */ + fiops_rb_erase(&ioc->rb_node, ioc->service_tree); + ioc->service_tree = NULL; + } + + fiops_log_ioc(fiopsd, ioc, "service tree add, vios %lld", vios); + + left = 1; + parent = NULL; + ioc->service_tree = service_tree; + p = &service_tree->rb.rb_node; + while (*p) { + struct rb_node **n; + + parent = *p; + __ioc = rb_entry(parent, struct fiops_ioc, rb_node); + + /* + * sort by key, that represents service time. + */ + if (vios < __ioc->vios) + n = &(*p)->rb_left; + else { + n = &(*p)->rb_right; + left = 0; + } + + p = n; + } + + if (left) + service_tree->left = &ioc->rb_node; + + ioc->vios = vios; + rb_link_node(&ioc->rb_node, parent, p); + rb_insert_color(&ioc->rb_node, &service_tree->rb); + service_tree->count++; + + fiops_update_min_vios(service_tree); +} + +/* + * Update ioc's position in the service tree. + */ +static void fiops_resort_rr_list(struct fiops_data *fiopsd, + struct fiops_ioc *ioc) +{ + /* + * Resorting requires the ioc to be on the RR list already. + */ + if (fiops_ioc_on_rr(ioc)) + fiops_service_tree_add(fiopsd, ioc); +} + +/* + * add to busy list of queues for service, trying to be fair in ordering + * the pending list according to last request service + */ +static void fiops_add_ioc_rr(struct fiops_data *fiopsd, struct fiops_ioc *ioc) +{ + BUG_ON(fiops_ioc_on_rr(ioc)); + fiops_mark_ioc_on_rr(ioc); + + fiopsd->busy_queues++; + + fiops_resort_rr_list(fiopsd, ioc); +} + +/* + * Called when the ioc no longer has requests pending, remove it from + * the service tree. + */ +static void fiops_del_ioc_rr(struct fiops_data *fiopsd, struct fiops_ioc *ioc) +{ + BUG_ON(!fiops_ioc_on_rr(ioc)); + fiops_clear_ioc_on_rr(ioc); + + if (!RB_EMPTY_NODE(&ioc->rb_node)) { + fiops_rb_erase(&ioc->rb_node, ioc->service_tree); + ioc->service_tree = NULL; + } + + BUG_ON(!fiopsd->busy_queues); + fiopsd->busy_queues--; +} + +/* + * rb tree support functions + */ +static void fiops_del_rq_rb(struct request *rq) +{ + struct fiops_ioc *ioc = RQ_CIC(rq); + + elv_rb_del(&ioc->sort_list, rq); +} + +static void fiops_add_rq_rb(struct request *rq) +{ + struct fiops_ioc *ioc = RQ_CIC(rq); + struct fiops_data *fiopsd = ioc->fiopsd; + + elv_rb_add(&ioc->sort_list, rq); + + if (!fiops_ioc_on_rr(ioc)) + fiops_add_ioc_rr(fiopsd, ioc); +} + +static void fiops_reposition_rq_rb(struct fiops_ioc *ioc, struct request *rq) +{ + elv_rb_del(&ioc->sort_list, rq); + fiops_add_rq_rb(rq); +} + +static void fiops_remove_request(struct request *rq) +{ + list_del_init(&rq->queuelist); + fiops_del_rq_rb(rq); +} + +static u64 fiops_scaled_vios(struct fiops_data *fiopsd, + struct fiops_ioc *ioc, struct request *rq) +{ + int vios = VIOS_SCALE; + + if (rq_data_dir(rq) == WRITE) + vios = vios * fiopsd->write_scale / fiopsd->read_scale; + + if (!rq_is_sync(rq)) + vios = vios * fiopsd->async_scale / fiopsd->sync_scale; + + vios += vios * (ioc->ioprio - IOPRIO_NORM) / VIOS_PRIO_SCALE; + + return vios; +} + +/* return vios dispatched */ +static u64 fiops_dispatch_request(struct fiops_data *fiopsd, + struct fiops_ioc *ioc) +{ + struct request *rq; + struct request_queue *q = fiopsd->queue; + + rq = rq_entry_fifo(ioc->fifo.next); + + fiops_remove_request(rq); + elv_dispatch_add_tail(q, rq); + + fiopsd->in_flight[rq_is_sync(rq)]++; + ioc->in_flight++; + + return fiops_scaled_vios(fiopsd, ioc, rq); +} + +static int fiops_forced_dispatch(struct fiops_data *fiopsd) +{ + struct fiops_ioc *ioc; + int dispatched = 0; + int i; + + for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) { + while (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) { + ioc = fiops_rb_first(&fiopsd->service_tree[i]); + + while (!list_empty(&ioc->fifo)) { + fiops_dispatch_request(fiopsd, ioc); + dispatched++; + } + if (fiops_ioc_on_rr(ioc)) + fiops_del_ioc_rr(fiopsd, ioc); + } + } + return dispatched; +} + +static struct fiops_ioc *fiops_select_ioc(struct fiops_data *fiopsd) +{ + struct fiops_ioc *ioc; + struct fiops_rb_root *service_tree = NULL; + int i; + struct request *rq; + + for (i = RT_WORKLOAD; i >= IDLE_WORKLOAD; i--) { + if (!RB_EMPTY_ROOT(&fiopsd->service_tree[i].rb)) { + service_tree = &fiopsd->service_tree[i]; + break; + } + } + + if (!service_tree) + return NULL; + + ioc = fiops_rb_first(service_tree); + + rq = rq_entry_fifo(ioc->fifo.next); + /* + * we are the only async task and sync requests are in flight, delay a + * moment. If there are other tasks coming, sync tasks have no chance + * to be starved, don't delay + */ + if (!rq_is_sync(rq) && fiopsd->in_flight[1] != 0 && + service_tree->count == 1) { + fiops_log_ioc(fiopsd, ioc, + "postpone async, in_flight async %d sync %d", + fiopsd->in_flight[0], fiopsd->in_flight[1]); + return NULL; + } + + return ioc; +} + +static void fiops_charge_vios(struct fiops_data *fiopsd, + struct fiops_ioc *ioc, u64 vios) +{ + struct fiops_rb_root *service_tree = ioc->service_tree; + ioc->vios += vios; + + fiops_log_ioc(fiopsd, ioc, "charge vios %lld, new vios %lld", vios, ioc->vios); + + if (RB_EMPTY_ROOT(&ioc->sort_list)) + fiops_del_ioc_rr(fiopsd, ioc); + else + fiops_resort_rr_list(fiopsd, ioc); + + fiops_update_min_vios(service_tree); +} + +static int fiops_dispatch_requests(struct request_queue *q, int force) +{ + struct fiops_data *fiopsd = q->elevator->elevator_data; + struct fiops_ioc *ioc; + u64 vios; + + if (unlikely(force)) + return fiops_forced_dispatch(fiopsd); + + ioc = fiops_select_ioc(fiopsd); + if (!ioc) + return 0; + + vios = fiops_dispatch_request(fiopsd, ioc); + + fiops_charge_vios(fiopsd, ioc, vios); + return 1; +} + +static void fiops_init_prio_data(struct fiops_ioc *cic) +{ + struct task_struct *tsk = current; + struct io_context *ioc = cic->icq.ioc; + int ioprio_class; + + if (!fiops_ioc_prio_changed(cic)) + return; + + ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio); + switch (ioprio_class) { + default: + printk(KERN_ERR "fiops: bad prio %x\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * no prio set, inherit CPU scheduling settings + */ + cic->ioprio = task_nice_ioprio(tsk); + cic->wl_type = fiops_wl_type(task_nice_ioclass(tsk)); + break; + case IOPRIO_CLASS_RT: + cic->ioprio = task_ioprio(ioc); + cic->wl_type = fiops_wl_type(IOPRIO_CLASS_RT); + break; + case IOPRIO_CLASS_BE: + cic->ioprio = task_ioprio(ioc); + cic->wl_type = fiops_wl_type(IOPRIO_CLASS_BE); + break; + case IOPRIO_CLASS_IDLE: + cic->wl_type = fiops_wl_type(IOPRIO_CLASS_IDLE); + cic->ioprio = 7; + break; + } + + fiops_clear_ioc_prio_changed(cic); +} + +static void fiops_insert_request(struct request_queue *q, struct request *rq) +{ + struct fiops_ioc *ioc = RQ_CIC(rq); + + fiops_init_prio_data(ioc); + + list_add_tail(&rq->queuelist, &ioc->fifo); + + fiops_add_rq_rb(rq); +} + +/* + * scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing + */ +static inline void fiops_schedule_dispatch(struct fiops_data *fiopsd) +{ + if (fiopsd->busy_queues) + kblockd_schedule_work(fiopsd->queue, &fiopsd->unplug_work); +} + +static void fiops_completed_request(struct request_queue *q, struct request *rq) +{ + struct fiops_data *fiopsd = q->elevator->elevator_data; + struct fiops_ioc *ioc = RQ_CIC(rq); + + fiopsd->in_flight[rq_is_sync(rq)]--; + ioc->in_flight--; + + fiops_log_ioc(fiopsd, ioc, "in_flight %d, busy queues %d", + ioc->in_flight, fiopsd->busy_queues); + + if (fiopsd->in_flight[0] + fiopsd->in_flight[1] == 0) + fiops_schedule_dispatch(fiopsd); +} + +static struct request * +fiops_find_rq_fmerge(struct fiops_data *fiopsd, struct bio *bio) +{ + struct task_struct *tsk = current; + struct fiops_ioc *cic; + + cic = fiops_cic_lookup(fiopsd, tsk->io_context); + + if (cic) { + sector_t sector = bio->bi_sector + bio_sectors(bio); + + return elv_rb_find(&cic->sort_list, sector); + } + + return NULL; +} + +static int fiops_merge(struct request_queue *q, struct request **req, + struct bio *bio) +{ + struct fiops_data *fiopsd = q->elevator->elevator_data; + struct request *__rq; + + __rq = fiops_find_rq_fmerge(fiopsd, bio); + if (__rq && elv_rq_merge_ok(__rq, bio)) { + *req = __rq; + return ELEVATOR_FRONT_MERGE; + } + + return ELEVATOR_NO_MERGE; +} + +static void fiops_merged_request(struct request_queue *q, struct request *req, + int type) +{ + if (type == ELEVATOR_FRONT_MERGE) { + struct fiops_ioc *ioc = RQ_CIC(req); + + fiops_reposition_rq_rb(ioc, req); + } +} + +static void +fiops_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + struct fiops_ioc *ioc = RQ_CIC(rq); + struct fiops_data *fiopsd = q->elevator->elevator_data; + + fiops_remove_request(next); + + ioc = RQ_CIC(next); + /* + * all requests of this task are merged to other tasks, delete it + * from the service tree. + */ + if (fiops_ioc_on_rr(ioc) && RB_EMPTY_ROOT(&ioc->sort_list)) + fiops_del_ioc_rr(fiopsd, ioc); +} + +static int fiops_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) +{ + struct fiops_data *fiopsd = q->elevator->elevator_data; + struct fiops_ioc *cic; + + /* + * Lookup the ioc that this bio will be queued with. Allow + * merge only if rq is queued there. + */ + cic = fiops_cic_lookup(fiopsd, current->io_context); + + return cic == RQ_CIC(rq); +} + +static void fiops_exit_queue(struct elevator_queue *e) +{ + struct fiops_data *fiopsd = e->elevator_data; + + cancel_work_sync(&fiopsd->unplug_work); + + kfree(fiopsd); +} + +static void fiops_kick_queue(struct work_struct *work) +{ + struct fiops_data *fiopsd = + container_of(work, struct fiops_data, unplug_work); + struct request_queue *q = fiopsd->queue; + + spin_lock_irq(q->queue_lock); + __blk_run_queue(q); + spin_unlock_irq(q->queue_lock); +} + +static void *fiops_init_queue(struct request_queue *q) +{ + struct fiops_data *fiopsd; + int i; + + fiopsd = kzalloc_node(sizeof(*fiopsd), GFP_KERNEL, q->node); + if (!fiopsd) + return NULL; + + fiopsd->queue = q; + + for (i = IDLE_WORKLOAD; i <= RT_WORKLOAD; i++) + fiopsd->service_tree[i] = FIOPS_RB_ROOT; + + INIT_WORK(&fiopsd->unplug_work, fiops_kick_queue); + + fiopsd->read_scale = VIOS_READ_SCALE; + fiopsd->write_scale = VIOS_WRITE_SCALE; + fiopsd->sync_scale = VIOS_SYNC_SCALE; + fiopsd->async_scale = VIOS_ASYNC_SCALE; + + return fiopsd; +} + +static void fiops_init_icq(struct io_cq *icq) +{ + struct fiops_data *fiopsd = icq->q->elevator->elevator_data; + struct fiops_ioc *ioc = icq_to_cic(icq); + + RB_CLEAR_NODE(&ioc->rb_node); + INIT_LIST_HEAD(&ioc->fifo); + ioc->sort_list = RB_ROOT; + + ioc->fiopsd = fiopsd; + + ioc->pid = current->pid; + fiops_mark_ioc_prio_changed(ioc); +} + +/* + * sysfs parts below --> + */ +static ssize_t +fiops_var_show(unsigned int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t +fiops_var_store(unsigned int *var, const char *page, size_t count) +{ + char *p = (char *) page; + + *var = simple_strtoul(p, &p, 10); + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct fiops_data *fiopsd = e->elevator_data; \ + return fiops_var_show(__VAR, (page)); \ +} +SHOW_FUNCTION(fiops_read_scale_show, fiopsd->read_scale); +SHOW_FUNCTION(fiops_write_scale_show, fiopsd->write_scale); +SHOW_FUNCTION(fiops_sync_scale_show, fiopsd->sync_scale); +SHOW_FUNCTION(fiops_async_scale_show, fiopsd->async_scale); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX) \ +static ssize_t __FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct fiops_data *fiopsd = e->elevator_data; \ + unsigned int __data; \ + int ret = fiops_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(fiops_read_scale_store, &fiopsd->read_scale, 1, 100); +STORE_FUNCTION(fiops_write_scale_store, &fiopsd->write_scale, 1, 100); +STORE_FUNCTION(fiops_sync_scale_store, &fiopsd->sync_scale, 1, 100); +STORE_FUNCTION(fiops_async_scale_store, &fiopsd->async_scale, 1, 100); +#undef STORE_FUNCTION + +#define FIOPS_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, fiops_##name##_show, fiops_##name##_store) + +static struct elv_fs_entry fiops_attrs[] = { + FIOPS_ATTR(read_scale), + FIOPS_ATTR(write_scale), + FIOPS_ATTR(sync_scale), + FIOPS_ATTR(async_scale), + __ATTR_NULL +}; + +static struct elevator_type iosched_fiops = { + .ops = { + .elevator_merge_fn = fiops_merge, + .elevator_merged_fn = fiops_merged_request, + .elevator_merge_req_fn = fiops_merged_requests, + .elevator_allow_merge_fn = fiops_allow_merge, + .elevator_dispatch_fn = fiops_dispatch_requests, + .elevator_add_req_fn = fiops_insert_request, + .elevator_completed_req_fn = fiops_completed_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_init_icq_fn = fiops_init_icq, + .elevator_init_fn = fiops_init_queue, + .elevator_exit_fn = fiops_exit_queue, + }, + .icq_size = sizeof(struct fiops_ioc), + .icq_align = __alignof__(struct fiops_ioc), + .elevator_attrs = fiops_attrs, + .elevator_name = "fiops", + .elevator_owner = THIS_MODULE, +}; + +static int __init fiops_init(void) +{ + return elv_register(&iosched_fiops); +} + +static void __exit fiops_exit(void) +{ + elv_unregister(&iosched_fiops); +} + +module_init(fiops_init); +module_exit(fiops_exit); + +MODULE_AUTHOR("Jens Axboe, Shaohua Li "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IOPS based IO scheduler"); From f63d30a4597ce567d4d6f4faab728c8fb21c415a Mon Sep 17 00:00:00 2001 From: vm03 Date: Fri, 4 Sep 2015 11:02:15 +0300 Subject: [PATCH 098/104] Enable FIOPS Change-Id: I85b1840367272d227f3df25f1d79579a317a4bf7 --- arch/arm/configs/w3ds_global_com_defconfig | 1 + arch/arm/configs/w5n_global_com_defconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/configs/w3ds_global_com_defconfig b/arch/arm/configs/w3ds_global_com_defconfig index 36bfcdadf962..794a164443f8 100755 --- a/arch/arm/configs/w3ds_global_com_defconfig +++ b/arch/arm/configs/w3ds_global_com_defconfig @@ -37,6 +37,7 @@ CONFIG_MODVERSIONS=y CONFIG_PARTITION_ADVANCED=y CONFIG_EFI_PARTITION=y CONFIG_IOSCHED_TEST=m +CONFIG_IOSCHED_FIOPS=y CONFIG_ARCH_MSM=y CONFIG_ARCH_MSM8610=y CONFIG_MACH_MSM8X10_W3DS_GLOBAL_COM=y diff --git a/arch/arm/configs/w5n_global_com_defconfig b/arch/arm/configs/w5n_global_com_defconfig index 8efb702fd6ff..7c99522c8395 100644 --- a/arch/arm/configs/w5n_global_com_defconfig +++ b/arch/arm/configs/w5n_global_com_defconfig @@ -36,6 +36,7 @@ CONFIG_MODVERSIONS=y CONFIG_PARTITION_ADVANCED=y CONFIG_EFI_PARTITION=y CONFIG_IOSCHED_TEST=y +CONFIG_IOSCHED_FIOPS=y CONFIG_ARCH_MSM=y CONFIG_ARCH_MSM8610=y CONFIG_MACH_MSM8X10_W5=y From 6a2ecc3bd29ccd1b5c89bffe2bc56f4e278a54c4 Mon Sep 17 00:00:00 2001 From: myfluxi Date: Sun, 9 Feb 2014 21:33:28 +0100 Subject: [PATCH 099/104] PM: devfreq: Fix simple_ondemand crashing on startup simple_ondemands private data must be set to NULL, otherwise we would run into a NULL pointer in kgsl_devfreq_get_dev_status(). Change-Id: Id494f1b65cb674fee56dae958bc1da267ed15501 --- drivers/devfreq/governor_simpleondemand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index d37997da89b1..6ae9f86523b2 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -23,7 +23,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, u32 *flag) { struct devfreq_dev_status stat; - int err = df->profile->get_dev_status(df->dev.parent, &stat); + int err; unsigned long long a, b; unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; @@ -31,6 +31,9 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX; unsigned long min = (df->min_freq) ? df->min_freq : 0; + stat.private_data = NULL; + + err = df->profile->get_dev_status(df->dev.parent, &stat); if (err) return err; From ae4814595b8fdd29646bcbfd9994052b2792d60f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 9 Sep 2015 21:55:07 -0700 Subject: [PATCH 100/104] tcp_cubic: better follow cubic curve after idle period Jana Iyengar found an interesting issue on CUBIC : The epoch is only updated/reset initially and when experiencing losses. The delta "t" of now - epoch_start can be arbitrary large after app idle as well as the bic_target. Consequentially the slope (inverse of ca->cnt) would be really large, and eventually ca->cnt would be lower-bounded in the end to 2 to have delayed-ACK slow-start behavior. This particularly shows up when slow_start_after_idle is disabled as a dangerous cwnd inflation (1.5 x RTT) after few seconds of idle time. Jana initial fix was to reset epoch_start if app limited, but Neal pointed out it would ask the CUBIC algorithm to recalculate the curve so that we again start growing steeply upward from where cwnd is now (as CUBIC does just after a loss). Ideally we'd want the cwnd growth curve to be the same shape, just shifted later in time by the amount of the idle period. Change-Id: I5a6b57d38d85c1e685835061888e719d240350dc Reported-by: Jana Iyengar Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Cc: Stephen Hemminger Cc: Sangtae Ha Cc: Lawrence Brakmo Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index a9077f441cb2..651444442936 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -153,6 +153,21 @@ static void bictcp_init(struct sock *sk) tcp_sk(sk)->snd_ssthresh = initial_ssthresh; } +static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_TX_START) { + s32 delta = tcp_time_stamp - tcp_sk(sk)->lsndtime; + struct bictcp *ca = inet_csk_ca(sk); + + /* We were application limited (idle) for a while. + * Shift epoch_start to keep cwnd growth to cubic curve. + */ + if (ca->epoch_start && delta > 0) + ca->epoch_start += delta; + return; + } +} + /* calculate the cubic root of x using a table lookup followed by one * Newton-Raphson iteration. * Avg err ~= 0.195% @@ -437,6 +452,7 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, .undo_cwnd = bictcp_undo_cwnd, + .cwnd_event = bictcp_cwnd_event, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, .name = "cubic", From 032ed5723906bd54cfd0876fac2135a0e527cc57 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 17 Sep 2015 08:38:00 -0700 Subject: [PATCH 101/104] tcp_cubic: do not set epoch_start in the future Tracking idle time in bictcp_cwnd_event() is imprecise, as epoch_start is normally set at ACK processing time, not at send time. Doing a proper fix would need to add an additional state variable, and does not seem worth the trouble, given CUBIC bug has been there forever before Jana noticed it. Let's simply not set epoch_start in the future, otherwise bictcp_update() could overflow and CUBIC would again grow cwnd too fast. This was detected thanks to a packetdrill test Neal wrote that was flaky before applying this fix. Change-Id: Ifd1e4be175824a31619ff4c1dc973f82346b799d Fixes: 30927520dbae ("tcp_cubic: better follow cubic curve after idle period") Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Cc: Jana Iyengar Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 651444442936..bde7beba65bd 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -156,14 +156,20 @@ static void bictcp_init(struct sock *sk) static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event) { if (event == CA_EVENT_TX_START) { - s32 delta = tcp_time_stamp - tcp_sk(sk)->lsndtime; struct bictcp *ca = inet_csk_ca(sk); + u32 now = tcp_time_stamp; + s32 delta; + + delta = now - tcp_sk(sk)->lsndtime; /* We were application limited (idle) for a while. * Shift epoch_start to keep cwnd growth to cubic curve. */ - if (ca->epoch_start && delta > 0) + if (ca->epoch_start && delta > 0) { ca->epoch_start += delta; + if (after(ca->epoch_start, now)) + ca->epoch_start = now; + } return; } } From 31c0b013e1e7eb1fe8aaef9cae68eccec38ede36 Mon Sep 17 00:00:00 2001 From: Muhammed Siju Date: Tue, 7 Jul 2015 19:59:41 +0530 Subject: [PATCH 102/104] net: rmnet_data: Add header files for rmnet_data Add new rmnet_data.h header file to fix compilation. Change-Id: I919c1c687eafc1dd88ae8ddf3b9a1e2b96d2a12a Signed-off-by: Muhammed Siju --- include/linux/Kbuild | 1 + include/linux/msm_rmnet.h | 111 +++++++++++++++++ include/linux/rmnet_data.h | 236 +++++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 include/linux/rmnet_data.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 8111fd4cb71c..59fc7bfb851e 100755 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -444,6 +444,7 @@ header-y += msm_audio_sbc.h header-y += msm_ipc.h header-y += msm_charm.h header-y += msm_rmnet.h +header-y += rmnet_data.h header-y += qseecom.h header-y += qcedev.h header-y += idle_stats_device.h diff --git a/include/linux/msm_rmnet.h b/include/linux/msm_rmnet.h index d41b55c98afd..08cd3b67ece2 100644 --- a/include/linux/msm_rmnet.h +++ b/include/linux/msm_rmnet.h @@ -29,9 +29,114 @@ enum rmnet_ioctl_cmds_e { RMNET_IOCTL_CLOSE = 0x000089F9, /* Close transport port */ RMNET_IOCTL_FLOW_ENABLE = 0x000089FA, /* Flow enable */ RMNET_IOCTL_FLOW_DISABLE = 0x000089FB, /* Flow disable */ + RMNET_IOCTL_FLOW_SET_HNDL = 0x000089FC, /* Set flow handle */ + RMNET_IOCTL_EXTENDED = 0x000089FD, /* Extended IOCTLs */ RMNET_IOCTL_MAX }; +enum rmnet_ioctl_extended_cmds_e { +/* RmNet Data Required IOCTLs */ + RMNET_IOCTL_GET_SUPPORTED_FEATURES = 0x0000, /* Get features */ + RMNET_IOCTL_SET_MRU = 0x0001, /* Set MRU */ + RMNET_IOCTL_GET_MRU = 0x0002, /* Get MRU */ + RMNET_IOCTL_GET_EPID = 0x0003, /* Get endpoint ID */ + RMNET_IOCTL_GET_DRIVER_NAME = 0x0004, /* Get driver name */ + RMNET_IOCTL_ADD_MUX_CHANNEL = 0x0005, /* Add MUX ID */ + RMNET_IOCTL_SET_EGRESS_DATA_FORMAT = 0x0006, /* Set EDF */ + RMNET_IOCTL_SET_INGRESS_DATA_FORMAT = 0x0007, /* Set IDF */ + RMNET_IOCTL_SET_AGGREGATION_COUNT = 0x0008, /* Set agg count */ + RMNET_IOCTL_GET_AGGREGATION_COUNT = 0x0009, /* Get agg count */ + RMNET_IOCTL_SET_AGGREGATION_SIZE = 0x000A, /* Set agg size */ + RMNET_IOCTL_GET_AGGREGATION_SIZE = 0x000B, /* Get agg size */ + RMNET_IOCTL_FLOW_CONTROL = 0x000C, /* Do flow control */ + RMNET_IOCTL_GET_DFLT_CONTROL_CHANNEL = 0x000D, /* For legacy use */ + RMNET_IOCTL_GET_HWSW_MAP = 0x000E, /* Get HW/SW map */ + RMNET_IOCTL_SET_RX_HEADROOM = 0x000F, /* RX Headroom */ + RMNET_IOCTL_GET_EP_PAIR = 0x0010, /* Endpoint pair */ + RMNET_IOCTL_SET_QOS_VERSION = 0x0011, /* 8/6 byte QoS hdr*/ + RMNET_IOCTL_GET_QOS_VERSION = 0x0012, /* 8/6 byte QoS hdr*/ + RMNET_IOCTL_GET_SUPPORTED_QOS_MODES = 0x0013, /* Get QoS modes */ + RMNET_IOCTL_SET_SLEEP_STATE = 0x0014, /* Set sleep state */ + RMNET_IOCTL_SET_XLAT_DEV_INFO = 0x0015, /* xlat dev name */ + RMNET_IOCTL_EXTENDED_MAX = 0x0016 +}; + +/* Return values for the RMNET_IOCTL_GET_SUPPORTED_FEATURES IOCTL */ +#define RMNET_IOCTL_FEAT_NOTIFY_MUX_CHANNEL (1<<0) +#define RMNET_IOCTL_FEAT_SET_EGRESS_DATA_FORMAT (1<<1) +#define RMNET_IOCTL_FEAT_SET_INGRESS_DATA_FORMAT (1<<2) +#define RMNET_IOCTL_FEAT_SET_AGGREGATION_COUNT (1<<3) +#define RMNET_IOCTL_FEAT_GET_AGGREGATION_COUNT (1<<4) +#define RMNET_IOCTL_FEAT_SET_AGGREGATION_SIZE (1<<5) +#define RMNET_IOCTL_FEAT_GET_AGGREGATION_SIZE (1<<6) +#define RMNET_IOCTL_FEAT_FLOW_CONTROL (1<<7) +#define RMNET_IOCTL_FEAT_GET_DFLT_CONTROL_CHANNEL (1<<8) +#define RMNET_IOCTL_FEAT_GET_HWSW_MAP (1<<9) + +/* Input values for the RMNET_IOCTL_SET_EGRESS_DATA_FORMAT IOCTL */ +#define RMNET_IOCTL_EGRESS_FORMAT_MAP (1<<1) +#define RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION (1<<2) +#define RMNET_IOCTL_EGRESS_FORMAT_MUXING (1<<3) +#define RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM (1<<4) + +/* Input values for the RMNET_IOCTL_SET_INGRESS_DATA_FORMAT IOCTL */ +#define RMNET_IOCTL_INGRESS_FORMAT_MAP (1<<1) +#define RMNET_IOCTL_INGRESS_FORMAT_DEAGGREGATION (1<<2) +#define RMNET_IOCTL_INGRESS_FORMAT_DEMUXING (1<<3) +#define RMNET_IOCTL_INGRESS_FORMAT_CHECKSUM (1<<4) +#define RMNET_IOCTL_INGRESS_FORMAT_AGG_DATA (1<<5) + +/* User space may not have this defined. */ +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +struct rmnet_ioctl_extended_s { + uint32_t extended_ioctl; + union { + uint32_t data; /* Generic data field for most extended IOCTLs */ + + /* Return values for + * RMNET_IOCTL_GET_DRIVER_NAME + * RMNET_IOCTL_GET_DFLT_CONTROL_CHANNEL */ + int8_t if_name[IFNAMSIZ]; + + /* Input values for the RMNET_IOCTL_ADD_MUX_CHANNEL IOCTL */ + struct { + uint32_t mux_id; + int8_t vchannel_name[IFNAMSIZ]; + } rmnet_mux_val; + + /* Input values for the RMNET_IOCTL_FLOW_CONTROL IOCTL */ + struct { + uint8_t flow_mode; + uint8_t mux_id; + } flow_control_prop; + + /* Return values for RMNET_IOCTL_GET_EP_PAIR */ + struct { + uint32_t consumer_pipe_num; + uint32_t producer_pipe_num; + } ipa_ep_pair; + + struct { + uint32_t __data; /* Placeholder for legacy data*/ + uint32_t agg_size; + uint32_t agg_count; + } ingress_format; + } u; +}; + +struct rmnet_ioctl_data_s { + union { + uint32_t operation_mode; + uint32_t tcm_handle; + } u; +}; + +#define RMNET_IOCTL_QOS_MODE_6 (1<<0) +#define RMNET_IOCTL_QOS_MODE_8 (1<<1) + /* QMI QoS header definition */ #define QMI_QOS_HDR_S __attribute((__packed__)) qmi_qos_hdr_s struct QMI_QOS_HDR_S { @@ -40,4 +145,10 @@ struct QMI_QOS_HDR_S { unsigned long flow_id; }; +/* QMI QoS 8-byte header. */ +struct qmi_qos_hdr8_s { + struct QMI_QOS_HDR_S hdr; + uint8_t reserved[2]; +} __attribute((__packed__)); + #endif /* _MSM_RMNET_H_ */ diff --git a/include/linux/rmnet_data.h b/include/linux/rmnet_data.h new file mode 100644 index 000000000000..e0ca5116e7d9 --- /dev/null +++ b/include/linux/rmnet_data.h @@ -0,0 +1,236 @@ +#ifndef _RMNET_DATA_H_ +#define _RMNET_DATA_H_ + +/* ***************** Constants ********************************************** */ +#define RMNET_LOCAL_LOGICAL_ENDPOINT -1 + +#define RMNET_EGRESS_FORMAT__RESERVED__ (1<<0) +#define RMNET_EGRESS_FORMAT_MAP (1<<1) +#define RMNET_EGRESS_FORMAT_AGGREGATION (1<<2) +#define RMNET_EGRESS_FORMAT_MUXING (1<<3) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 (1<<4) + +#define RMNET_INGRESS_FIX_ETHERNET (1<<0) +#define RMNET_INGRESS_FORMAT_MAP (1<<1) +#define RMNET_INGRESS_FORMAT_DEAGGREGATION (1<<2) +#define RMNET_INGRESS_FORMAT_DEMUXING (1<<3) +#define RMNET_INGRESS_FORMAT_MAP_COMMANDS (1<<4) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 (1<<5) + +/* ***************** Netlink API ******************************************** */ +#define RMNET_NETLINK_PROTO 31 +#define RMNET_MAX_STR_LEN 16 +#define RMNET_NL_DATA_MAX_LEN 64 + +#define RMNET_NETLINK_MSG_COMMAND 0 +#define RMNET_NETLINK_MSG_RETURNCODE 1 +#define RMNET_NETLINK_MSG_RETURNDATA 2 + +struct rmnet_nl_msg_s { + uint16_t reserved; + uint16_t message_type; + uint16_t reserved2:14; + uint16_t crd:2; + union { + uint16_t arg_length; + uint16_t return_code; + }; + union { + uint8_t data[RMNET_NL_DATA_MAX_LEN]; + struct { + uint8_t dev[RMNET_MAX_STR_LEN]; + uint32_t flags; + uint16_t agg_size; + uint16_t agg_count; + uint8_t tail_spacing; + } data_format; + struct { + uint8_t dev[RMNET_MAX_STR_LEN]; + int32_t ep_id; + uint8_t operating_mode; + uint8_t next_dev[RMNET_MAX_STR_LEN]; + } local_ep_config; + struct { + uint32_t id; + uint8_t vnd_name[RMNET_MAX_STR_LEN]; + } vnd; + struct { + uint32_t id; + uint32_t map_flow_id; + uint32_t tc_flow_id; + } flow_control; + }; +}; + +enum rmnet_netlink_message_types_e { + /* + * RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE - Register RMNET data driver + * on a particular device. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: status code + */ + RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE, + + /* + * RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE - Unregister RMNET data + * driver on a particular + * device. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: status code + */ + RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE, + + /* + * RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED - Get if RMNET data + * driver is registered on a + * particular device. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: 1 if registered, 0 if not + */ + RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED, + + /* + * RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT - Sets the egress data + * format for a particular + * link. + * Args: uint32_t egress_flags + * char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: status code + */ + RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT, + + /* + * RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT - Gets the egress data + * format for a particular + * link. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: 4-bytes data: uint32_t egress_flags + */ + RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT, + + /* + * RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT - Sets the ingress data + * format for a particular + * link. + * Args: uint32_t ingress_flags + * char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: status code + */ + RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT, + + /* + * RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT - Gets the ingress data + * format for a particular + * link. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * Returns: 4-bytes data: uint32_t ingress_flags + */ + RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT, + + /* + * RMNET_NETLINK_SET_LOGICAL_EP_CONFIG - Sets the logical endpoint + * configuration for a particular + * link. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * int32_t logical_ep_id, valid values are -1 through 31 + * uint8_t rmnet_mode: one of none, vnd, bridged + * char[] egress_dev_name: Egress device if operating in bridge mode + * Returns: status code + */ + RMNET_NETLINK_SET_LOGICAL_EP_CONFIG, + + /* + * RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG - Un-sets the logical endpoint + * configuration for a particular + * link. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * int32_t logical_ep_id, valid values are -1 through 31 + * Returns: status code + */ + RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG, + + /* + * RMNET_NETLINK_GET_LOGICAL_EP_CONFIG - Gets the logical endpoint + * configuration for a particular + * link. + * Args: char[] dev_name: Null terminated ASCII string, max length: 15 + * int32_t logical_ep_id, valid values are -1 through 31 + * Returns: uint8_t rmnet_mode: one of none, vnd, bridged + * char[] egress_dev_name: Egress device + */ + RMNET_NETLINK_GET_LOGICAL_EP_CONFIG, + + /* + * RMNET_NETLINK_NEW_VND - Creates a new virtual network device node + * Args: int32_t node number + * Returns: status code + */ + RMNET_NETLINK_NEW_VND, + + /* + * RMNET_NETLINK_NEW_VND_WITH_PREFIX - Creates a new virtual network + * device node with the specified + * prefix for the device name + * Args: int32_t node number + * char[] vnd_name - Use as prefix + * Returns: status code + */ + RMNET_NETLINK_NEW_VND_WITH_PREFIX, + + /* + * RMNET_NETLINK_GET_VND_NAME - Gets the string name of a VND from ID + * Args: int32_t node number + * Returns: char[] vnd_name + */ + RMNET_NETLINK_GET_VND_NAME, + + /* + * RMNET_NETLINK_FREE_VND - Removes virtual network device node + * Args: int32_t node number + * Returns: status code + */ + RMNET_NETLINK_FREE_VND, + + /* + * RMNET_NETLINK_ADD_VND_TC_FLOW - Add flow control handle on VND + * Args: int32_t node number + * uint32_t MAP Flow Handle + * uint32_t TC Flow Handle + * Returns: status code + */ + RMNET_NETLINK_ADD_VND_TC_FLOW, + + /* + * RMNET_NETLINK_DEL_VND_TC_FLOW - Removes flow control handle on VND + * Args: int32_t node number + * uint32_t MAP Flow Handle + * Returns: status code + */ + RMNET_NETLINK_DEL_VND_TC_FLOW +}; + +enum rmnet_config_endpoint_modes_e { + /* Pass the frame up the stack with no modifications to skb->dev */ + RMNET_EPMODE_NONE, + /* Replace skb->dev to a virtual rmnet device and pass up the stack */ + RMNET_EPMODE_VND, + /* Pass the frame directly to another device with dev_queue_xmit(). */ + RMNET_EPMODE_BRIDGE, + /* Must be the last item in the list */ + RMNET_EPMODE_LENGTH +}; + +enum rmnet_config_return_codes_e { + RMNET_CONFIG_OK, + RMNET_CONFIG_UNKNOWN_MESSAGE, + RMNET_CONFIG_UNKNOWN_ERROR, + RMNET_CONFIG_NOMEM, + RMNET_CONFIG_DEVICE_IN_USE, + RMNET_CONFIG_INVALID_REQUEST, + RMNET_CONFIG_NO_SUCH_DEVICE, + RMNET_CONFIG_BAD_ARGUMENTS, + RMNET_CONFIG_BAD_EGRESS_DEVICE, + RMNET_CONFIG_TC_HANDLE_FULL +}; + +#endif /* _RMNET_DATA_H_ */ From e6ea87f6a9f5dd07b23c6290ccdc27944eac63b9 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Fri, 4 Sep 2015 18:33:39 -0600 Subject: [PATCH 103/104] include: rmnet_data: Define the MAPv4 data format Add an entry corresponding to the MAPv4 ingress and egress data format for checksum offload. Also update the copyright. Change-Id: Id2368d621f48ebf510371acf1502efb8bead65d2 Signed-off-by: Subash Abhinov Kasiviswanathan --- include/linux/rmnet_data.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/rmnet_data.h b/include/linux/rmnet_data.h index e0ca5116e7d9..92c04b3ad6c3 100644 --- a/include/linux/rmnet_data.h +++ b/include/linux/rmnet_data.h @@ -9,6 +9,7 @@ #define RMNET_EGRESS_FORMAT_AGGREGATION (1<<2) #define RMNET_EGRESS_FORMAT_MUXING (1<<3) #define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 (1<<4) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 (1<<5) #define RMNET_INGRESS_FIX_ETHERNET (1<<0) #define RMNET_INGRESS_FORMAT_MAP (1<<1) @@ -16,6 +17,7 @@ #define RMNET_INGRESS_FORMAT_DEMUXING (1<<3) #define RMNET_INGRESS_FORMAT_MAP_COMMANDS (1<<4) #define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 (1<<5) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 (1<<6) /* ***************** Netlink API ******************************************** */ #define RMNET_NETLINK_PROTO 31 From 2ed141afc5bc7b858ede6db4180e0801e30e1be4 Mon Sep 17 00:00:00 2001 From: Nguyen Nhat Truong <30290559+vatva691@users.noreply.github.com> Date: Wed, 30 Jan 2019 22:16:37 +0700 Subject: [PATCH 104/104] Update timeconst.pl --- kernel/timeconst.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/timeconst.pl b/kernel/timeconst.pl index eb51d76e058a..04612394c53e 100644 --- a/kernel/timeconst.pl +++ b/kernel/timeconst.pl @@ -370,7 +370,7 @@ (@) } @val = @{$canned_values{$hz}}; - if (!defined(@val)) { + if (!@val) { @val = compute_values($hz); } output($hz, @val);